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, base32
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 get_nickname_for_peerid(self, peerid):
54 def create_node_from_uri(self, auri):
55 u = uri.from_string(auri)
56 if (INewDirectoryURI.providedBy(u)
57 or IReadonlyNewDirectoryURI.providedBy(u)):
58 return FakeDirectoryNode(self).init_from_uri(u)
59 if IFileURI.providedBy(u):
60 return FakeCHKFileNode(u, self)
61 assert IMutableFileURI.providedBy(u), u
62 return FakeMutableFileNode(self).init_from_uri(u)
64 def create_empty_dirnode(self):
65 n = FakeDirectoryNode(self)
67 d.addCallback(lambda res: n)
70 MUTABLE_SIZELIMIT = FakeMutableFileNode.MUTABLE_SIZELIMIT
71 def create_mutable_file(self, contents=""):
72 n = FakeMutableFileNode(self)
73 return n.create(contents)
75 def upload(self, uploadable):
76 d = uploadable.get_size()
77 d.addCallback(lambda size: uploadable.read(size))
80 n = create_chk_filenode(self, data)
81 results = upload.UploadResults()
82 results.uri = n.get_uri()
84 d.addCallback(_got_data)
87 def list_all_upload_statuses(self):
88 return self._all_upload_status
89 def list_all_download_statuses(self):
90 return self._all_download_status
91 def list_all_mapupdate_statuses(self):
92 return self._all_mapupdate_statuses
93 def list_all_publish_statuses(self):
94 return self._all_publish_statuses
95 def list_all_retrieve_statuses(self):
96 return self._all_retrieve_statuses
97 def list_all_helper_statuses(self):
100 class HTTPClientHEADFactory(client.HTTPClientFactory):
101 def __init__(self, *args, **kwargs):
102 client.HTTPClientFactory.__init__(self, *args, **kwargs)
103 self.deferred.addCallback(lambda res: self.response_headers)
105 def noPage(self, reason):
106 # Twisted-2.5.0 and earlier had a bug, in which they would raise an
107 # exception when the response to a HEAD request had no body (when in
108 # fact they are defined to never have a body). This was fixed in
109 # Twisted-8.0 . To work around this, we catch the
110 # PartialDownloadError and make it disappear.
111 if (reason.check(client.PartialDownloadError)
112 and self.method.upper() == "HEAD"):
115 return client.HTTPClientFactory.noPage(self, reason)
118 class WebMixin(object):
120 self.s = FakeClient()
121 self.s.startService()
122 self.ws = s = webish.WebishServer("0")
123 s.setServiceParent(self.s)
124 self.webish_port = port = s.listener._port.getHost().port
125 self.webish_url = "http://localhost:%d" % port
127 l = [ self.s.create_empty_dirnode() for x in range(6) ]
128 d = defer.DeferredList(l)
130 self.public_root = res[0][1]
131 assert interfaces.IDirectoryNode.providedBy(self.public_root), res
132 self.public_url = "/uri/" + self.public_root.get_uri()
133 self.private_root = res[1][1]
137 self._foo_uri = foo.get_uri()
138 self._foo_readonly_uri = foo.get_readonly_uri()
139 # NOTE: we ignore the deferred on all set_uri() calls, because we
140 # know the fake nodes do these synchronously
141 self.public_root.set_uri(u"foo", foo.get_uri())
143 self.BAR_CONTENTS, n, self._bar_txt_uri = self.makefile(0)
144 foo.set_uri(u"bar.txt", self._bar_txt_uri)
146 foo.set_uri(u"empty", res[3][1].get_uri())
147 sub_uri = res[4][1].get_uri()
148 self._sub_uri = sub_uri
149 foo.set_uri(u"sub", sub_uri)
150 sub = self.s.create_node_from_uri(sub_uri)
152 _ign, n, blocking_uri = self.makefile(1)
153 foo.set_uri(u"blockingfile", blocking_uri)
155 unicode_filename = u"n\u00fc.txt" # n u-umlaut . t x t
156 # ok, unicode calls it LATIN SMALL LETTER U WITH DIAERESIS but I
157 # still think of it as an umlaut
158 foo.set_uri(unicode_filename, self._bar_txt_uri)
160 _ign, n, baz_file = self.makefile(2)
161 sub.set_uri(u"baz.txt", baz_file)
163 _ign, n, self._bad_file_uri = self.makefile(3)
164 # this uri should not be downloadable
165 del FakeCHKFileNode.all_contents[self._bad_file_uri]
168 self.public_root.set_uri(u"reedownlee", rodir.get_readonly_uri())
169 rodir.set_uri(u"nor", baz_file)
174 # public/foo/blockingfile
177 # public/foo/sub/baz.txt
179 # public/reedownlee/nor
180 self.NEWFILE_CONTENTS = "newfile contents\n"
182 return foo.get_metadata_for(u"bar.txt")
184 def _got_metadata(metadata):
185 self._bar_txt_metadata = metadata
186 d.addCallback(_got_metadata)
189 def makefile(self, number):
190 contents = "contents of file %s\n" % number
191 n = create_chk_filenode(self.s, contents)
192 return contents, n, n.get_uri()
195 return self.s.stopService()
197 def failUnlessIsBarDotTxt(self, res):
198 self.failUnlessEqual(res, self.BAR_CONTENTS, res)
200 def failUnlessIsBarJSON(self, res):
201 data = simplejson.loads(res)
202 self.failUnless(isinstance(data, list))
203 self.failUnlessEqual(data[0], u"filenode")
204 self.failUnless(isinstance(data[1], dict))
205 self.failIf(data[1]["mutable"])
206 self.failIf("rw_uri" in data[1]) # immutable
207 self.failUnlessEqual(data[1]["ro_uri"], self._bar_txt_uri)
208 self.failUnlessEqual(data[1]["size"], len(self.BAR_CONTENTS))
210 def failUnlessIsFooJSON(self, res):
211 data = simplejson.loads(res)
212 self.failUnless(isinstance(data, list))
213 self.failUnlessEqual(data[0], "dirnode", res)
214 self.failUnless(isinstance(data[1], dict))
215 self.failUnless(data[1]["mutable"])
216 self.failUnless("rw_uri" in data[1]) # mutable
217 self.failUnlessEqual(data[1]["rw_uri"], self._foo_uri)
218 self.failUnlessEqual(data[1]["ro_uri"], self._foo_readonly_uri)
220 kidnames = sorted([unicode(n) for n in data[1]["children"]])
221 self.failUnlessEqual(kidnames,
222 [u"bar.txt", u"blockingfile", u"empty",
223 u"n\u00fc.txt", u"sub"])
224 kids = dict( [(unicode(name),value)
226 in data[1]["children"].iteritems()] )
227 self.failUnlessEqual(kids[u"sub"][0], "dirnode")
228 self.failUnless("metadata" in kids[u"sub"][1])
229 self.failUnless("ctime" in kids[u"sub"][1]["metadata"])
230 self.failUnless("mtime" in kids[u"sub"][1]["metadata"])
231 self.failUnlessEqual(kids[u"bar.txt"][0], "filenode")
232 self.failUnlessEqual(kids[u"bar.txt"][1]["size"], len(self.BAR_CONTENTS))
233 self.failUnlessEqual(kids[u"bar.txt"][1]["ro_uri"], self._bar_txt_uri)
234 self.failUnlessEqual(kids[u"bar.txt"][1]["metadata"]["ctime"],
235 self._bar_txt_metadata["ctime"])
236 self.failUnlessEqual(kids[u"n\u00fc.txt"][1]["ro_uri"],
239 def GET(self, urlpath, followRedirect=False):
240 assert not isinstance(urlpath, unicode)
241 url = self.webish_url + urlpath
242 return client.getPage(url, method="GET", followRedirect=followRedirect)
244 def HEAD(self, urlpath):
245 # this requires some surgery, because twisted.web.client doesn't want
246 # to give us back the response headers.
247 factory = HTTPClientHEADFactory(urlpath, method="HEAD")
248 reactor.connectTCP("localhost", self.webish_port, factory)
249 return factory.deferred
251 def PUT(self, urlpath, data):
252 url = self.webish_url + urlpath
253 return client.getPage(url, method="PUT", postdata=data)
255 def DELETE(self, urlpath):
256 url = self.webish_url + urlpath
257 return client.getPage(url, method="DELETE")
259 def POST(self, urlpath, followRedirect=False, **fields):
260 url = self.webish_url + urlpath
261 sepbase = "boogabooga"
265 form.append('Content-Disposition: form-data; name="_charset"')
269 for name, value in fields.iteritems():
270 if isinstance(value, tuple):
271 filename, value = value
272 form.append('Content-Disposition: form-data; name="%s"; '
273 'filename="%s"' % (name, filename.encode("utf-8")))
275 form.append('Content-Disposition: form-data; name="%s"' % name)
277 if isinstance(value, unicode):
278 value = value.encode("utf-8")
281 assert isinstance(value, str)
285 body = "\r\n".join(form) + "\r\n"
286 headers = {"content-type": "multipart/form-data; boundary=%s" % sepbase,
288 return client.getPage(url, method="POST", postdata=body,
289 headers=headers, followRedirect=followRedirect)
291 def shouldFail(self, res, expected_failure, which,
292 substring=None, response_substring=None):
293 if isinstance(res, failure.Failure):
294 res.trap(expected_failure)
296 self.failUnless(substring in str(res),
297 "substring '%s' not in '%s'"
298 % (substring, str(res)))
299 if response_substring:
300 self.failUnless(response_substring in res.value.response,
301 "response substring '%s' not in '%s'"
302 % (response_substring, res.value.response))
304 self.fail("%s was supposed to raise %s, not get '%s'" %
305 (which, expected_failure, res))
307 def shouldFail2(self, expected_failure, which, substring,
309 callable, *args, **kwargs):
310 assert substring is None or isinstance(substring, str)
311 assert response_substring is None or isinstance(response_substring, str)
312 d = defer.maybeDeferred(callable, *args, **kwargs)
314 if isinstance(res, failure.Failure):
315 res.trap(expected_failure)
317 self.failUnless(substring in str(res),
318 "%s: substring '%s' not in '%s'"
319 % (which, substring, str(res)))
320 if response_substring:
321 self.failUnless(response_substring in res.value.response,
322 "%s: response substring '%s' not in '%s'"
324 response_substring, res.value.response))
326 self.fail("%s was supposed to raise %s, not get '%s'" %
327 (which, expected_failure, res))
331 def should404(self, res, which):
332 if isinstance(res, failure.Failure):
333 res.trap(error.Error)
334 self.failUnlessEqual(res.value.status, "404")
336 self.fail("%s was supposed to Error(404), not get '%s'" %
339 def shouldHTTPError(self, res, which, code=None, substring=None,
340 response_substring=None):
341 if isinstance(res, failure.Failure):
342 res.trap(error.Error)
344 self.failUnlessEqual(res.value.status, str(code))
346 self.failUnless(substring in str(res),
347 "substring '%s' not in '%s'"
348 % (substring, str(res)))
349 if response_substring:
350 self.failUnless(response_substring in res.value.response,
351 "response substring '%s' not in '%s'"
352 % (response_substring, res.value.response))
354 self.fail("%s was supposed to Error(%s), not get '%s'" %
357 def shouldHTTPError2(self, which,
358 code=None, substring=None, response_substring=None,
359 callable=None, *args, **kwargs):
360 assert substring is None or isinstance(substring, str)
362 d = defer.maybeDeferred(callable, *args, **kwargs)
363 d.addBoth(self.shouldHTTPError, which,
364 code, substring, response_substring)
368 class Web(WebMixin, testutil.StallMixin, unittest.TestCase):
369 def test_create(self):
372 def test_welcome(self):
375 self.failUnless('Welcome To AllMyData' in res)
376 self.failUnless('Tahoe' in res)
378 self.s.basedir = 'web/test_welcome'
379 fileutil.make_dirs("web/test_welcome")
380 fileutil.make_dirs("web/test_welcome/private")
382 d.addCallback(_check)
385 def test_provisioning_math(self):
386 self.failUnlessEqual(provisioning.binomial(10, 0), 1)
387 self.failUnlessEqual(provisioning.binomial(10, 1), 10)
388 self.failUnlessEqual(provisioning.binomial(10, 2), 45)
389 self.failUnlessEqual(provisioning.binomial(10, 9), 10)
390 self.failUnlessEqual(provisioning.binomial(10, 10), 1)
392 def test_provisioning(self):
393 d = self.GET("/provisioning/")
395 self.failUnless('Tahoe Provisioning Tool' in res)
396 fields = {'filled': True,
397 "num_users": int(50e3),
398 "files_per_user": 1000,
399 "space_per_user": int(1e9),
400 "sharing_ratio": 1.0,
401 "encoding_parameters": "3-of-10-5",
403 "ownership_mode": "A",
404 "download_rate": 100,
409 return self.POST("/provisioning/", **fields)
411 d.addCallback(_check)
413 self.failUnless('Tahoe Provisioning Tool' in res)
414 self.failUnless("Share space consumed: 167.01TB" in res)
416 fields = {'filled': True,
417 "num_users": int(50e6),
418 "files_per_user": 1000,
419 "space_per_user": int(5e9),
420 "sharing_ratio": 1.0,
421 "encoding_parameters": "25-of-100-50",
422 "num_servers": 30000,
423 "ownership_mode": "E",
424 "drive_failure_model": "U",
426 "download_rate": 1000,
431 return self.POST("/provisioning/", **fields)
432 d.addCallback(_check2)
434 self.failUnless("Share space consumed: huge!" in res)
435 fields = {'filled': True}
436 return self.POST("/provisioning/", **fields)
437 d.addCallback(_check3)
439 self.failUnless("Share space consumed:" in res)
440 d.addCallback(_check4)
443 def test_status(self):
444 dl_num = self.s.list_all_download_statuses()[0].get_counter()
445 ul_num = self.s.list_all_upload_statuses()[0].get_counter()
446 mu_num = self.s.list_all_mapupdate_statuses()[0].get_counter()
447 pub_num = self.s.list_all_publish_statuses()[0].get_counter()
448 ret_num = self.s.list_all_retrieve_statuses()[0].get_counter()
449 d = self.GET("/status", followRedirect=True)
451 self.failUnless('Upload and Download Status' in res, res)
452 self.failUnless('"down-%d"' % dl_num in res, res)
453 self.failUnless('"up-%d"' % ul_num in res, res)
454 self.failUnless('"mapupdate-%d"' % mu_num in res, res)
455 self.failUnless('"publish-%d"' % pub_num in res, res)
456 self.failUnless('"retrieve-%d"' % ret_num in res, res)
457 d.addCallback(_check)
458 d.addCallback(lambda res: self.GET("/status/?t=json"))
459 def _check_json(res):
460 data = simplejson.loads(res)
461 self.failUnless(isinstance(data, dict))
462 active = data["active"]
463 # TODO: test more. We need a way to fake an active operation
465 d.addCallback(_check_json)
467 d.addCallback(lambda res: self.GET("/status/down-%d" % dl_num))
469 self.failUnless("File Download Status" in res, res)
470 d.addCallback(_check_dl)
471 d.addCallback(lambda res: self.GET("/status/up-%d" % ul_num))
473 self.failUnless("File Upload Status" in res, res)
474 d.addCallback(_check_ul)
475 d.addCallback(lambda res: self.GET("/status/mapupdate-%d" % mu_num))
476 def _check_mapupdate(res):
477 self.failUnless("Mutable File Servermap Update Status" in res, res)
478 d.addCallback(_check_mapupdate)
479 d.addCallback(lambda res: self.GET("/status/publish-%d" % pub_num))
480 def _check_publish(res):
481 self.failUnless("Mutable File Publish Status" in res, res)
482 d.addCallback(_check_publish)
483 d.addCallback(lambda res: self.GET("/status/retrieve-%d" % ret_num))
484 def _check_retrieve(res):
485 self.failUnless("Mutable File Retrieve Status" in res, res)
486 d.addCallback(_check_retrieve)
490 def test_status_numbers(self):
491 drrm = status.DownloadResultsRendererMixin()
492 self.failUnlessEqual(drrm.render_time(None, None), "")
493 self.failUnlessEqual(drrm.render_time(None, 2.5), "2.50s")
494 self.failUnlessEqual(drrm.render_time(None, 0.25), "250ms")
495 self.failUnlessEqual(drrm.render_time(None, 0.0021), "2.1ms")
496 self.failUnlessEqual(drrm.render_time(None, 0.000123), "123us")
497 self.failUnlessEqual(drrm.render_rate(None, None), "")
498 self.failUnlessEqual(drrm.render_rate(None, 2500000), "2.50MBps")
499 self.failUnlessEqual(drrm.render_rate(None, 30100), "30.1kBps")
500 self.failUnlessEqual(drrm.render_rate(None, 123), "123Bps")
502 urrm = status.UploadResultsRendererMixin()
503 self.failUnlessEqual(urrm.render_time(None, None), "")
504 self.failUnlessEqual(urrm.render_time(None, 2.5), "2.50s")
505 self.failUnlessEqual(urrm.render_time(None, 0.25), "250ms")
506 self.failUnlessEqual(urrm.render_time(None, 0.0021), "2.1ms")
507 self.failUnlessEqual(urrm.render_time(None, 0.000123), "123us")
508 self.failUnlessEqual(urrm.render_rate(None, None), "")
509 self.failUnlessEqual(urrm.render_rate(None, 2500000), "2.50MBps")
510 self.failUnlessEqual(urrm.render_rate(None, 30100), "30.1kBps")
511 self.failUnlessEqual(urrm.render_rate(None, 123), "123Bps")
513 def test_GET_FILEURL(self):
514 d = self.GET(self.public_url + "/foo/bar.txt")
515 d.addCallback(self.failUnlessIsBarDotTxt)
518 def test_HEAD_FILEURL(self):
519 d = self.HEAD(self.public_url + "/foo/bar.txt")
521 self.failUnlessEqual(headers["content-length"][0],
522 str(len(self.BAR_CONTENTS)))
523 self.failUnlessEqual(headers["content-type"], ["text/plain"])
527 def test_GET_FILEURL_named(self):
528 base = "/file/%s" % urllib.quote(self._bar_txt_uri)
529 base2 = "/named/%s" % urllib.quote(self._bar_txt_uri)
530 d = self.GET(base + "/@@name=/blah.txt")
531 d.addCallback(self.failUnlessIsBarDotTxt)
532 d.addCallback(lambda res: self.GET(base + "/blah.txt"))
533 d.addCallback(self.failUnlessIsBarDotTxt)
534 d.addCallback(lambda res: self.GET(base + "/ignore/lots/blah.txt"))
535 d.addCallback(self.failUnlessIsBarDotTxt)
536 d.addCallback(lambda res: self.GET(base2 + "/@@name=/blah.txt"))
537 d.addCallback(self.failUnlessIsBarDotTxt)
538 save_url = base + "?save=true&filename=blah.txt"
539 d.addCallback(lambda res: self.GET(save_url))
540 d.addCallback(self.failUnlessIsBarDotTxt) # TODO: check headers
541 u_filename = u"n\u00e9wer.txt" # n e-acute w e r . t x t
542 u_fn_e = urllib.quote(u_filename.encode("utf-8"))
543 u_url = base + "?save=true&filename=" + u_fn_e
544 d.addCallback(lambda res: self.GET(u_url))
545 d.addCallback(self.failUnlessIsBarDotTxt) # TODO: check headers
548 def test_PUT_FILEURL_named_bad(self):
549 base = "/file/%s" % urllib.quote(self._bar_txt_uri)
550 d = self.shouldFail2(error.Error, "test_PUT_FILEURL_named_bad",
552 "/file can only be used with GET or HEAD",
553 self.PUT, base + "/@@name=/blah.txt", "")
556 def test_GET_DIRURL_named_bad(self):
557 base = "/file/%s" % urllib.quote(self._foo_uri)
558 d = self.shouldFail2(error.Error, "test_PUT_DIRURL_named_bad",
561 self.GET, base + "/@@name=/blah.txt")
564 def test_GET_slash_file_bad(self):
565 d = self.shouldFail2(error.Error, "test_GET_slash_file_bad",
567 "/file must be followed by a file-cap and a name",
571 def test_GET_unhandled_URI_named(self):
572 contents, n, newuri = self.makefile(12)
573 verifier_cap = n.get_verifier().to_string()
574 base = "/file/%s" % urllib.quote(verifier_cap)
575 # client.create_node_from_uri() can't handle verify-caps
576 d = self.shouldFail2(error.Error, "GET_unhandled_URI_named",
578 "is not a valid file- or directory- cap",
582 def test_GET_unhandled_URI(self):
583 contents, n, newuri = self.makefile(12)
584 verifier_cap = n.get_verifier().to_string()
585 base = "/uri/%s" % urllib.quote(verifier_cap)
586 # client.create_node_from_uri() can't handle verify-caps
587 d = self.shouldFail2(error.Error, "test_GET_unhandled_URI",
589 "is not a valid file- or directory- cap",
593 def test_GET_FILE_URI(self):
594 base = "/uri/%s" % urllib.quote(self._bar_txt_uri)
596 d.addCallback(self.failUnlessIsBarDotTxt)
599 def test_GET_FILE_URI_badchild(self):
600 base = "/uri/%s/boguschild" % urllib.quote(self._bar_txt_uri)
601 errmsg = "Files have no children, certainly not named 'boguschild'"
602 d = self.shouldFail2(error.Error, "test_GET_FILE_URI_badchild",
603 "400 Bad Request", errmsg,
607 def test_PUT_FILE_URI_badchild(self):
608 base = "/uri/%s/boguschild" % urllib.quote(self._bar_txt_uri)
609 errmsg = "Cannot create directory 'boguschild', because its parent is a file, not a directory"
610 d = self.shouldFail2(error.Error, "test_GET_FILE_URI_badchild",
611 "400 Bad Request", errmsg,
615 def test_GET_FILEURL_save(self):
616 d = self.GET(self.public_url + "/foo/bar.txt?filename=bar.txt&save=true")
617 # TODO: look at the headers, expect a Content-Disposition: attachment
619 d.addCallback(self.failUnlessIsBarDotTxt)
622 def test_GET_FILEURL_missing(self):
623 d = self.GET(self.public_url + "/foo/missing")
624 d.addBoth(self.should404, "test_GET_FILEURL_missing")
627 def test_PUT_NEWFILEURL(self):
628 d = self.PUT(self.public_url + "/foo/new.txt", self.NEWFILE_CONTENTS)
629 # TODO: we lose the response code, so we can't check this
630 #self.failUnlessEqual(responsecode, 201)
631 d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"new.txt")
632 d.addCallback(lambda res:
633 self.failUnlessChildContentsAre(self._foo_node, u"new.txt",
634 self.NEWFILE_CONTENTS))
637 def test_PUT_NEWFILEURL_mutable(self):
638 d = self.PUT(self.public_url + "/foo/new.txt?mutable=true",
639 self.NEWFILE_CONTENTS)
640 # TODO: we lose the response code, so we can't check this
641 #self.failUnlessEqual(responsecode, 201)
643 u = uri.from_string_mutable_filenode(res)
644 self.failUnless(u.is_mutable())
645 self.failIf(u.is_readonly())
647 d.addCallback(_check_uri)
648 d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"new.txt")
649 d.addCallback(lambda res:
650 self.failUnlessMutableChildContentsAre(self._foo_node,
652 self.NEWFILE_CONTENTS))
655 def test_PUT_NEWFILEURL_mutable_toobig(self):
656 d = self.shouldFail2(error.Error, "test_PUT_NEWFILEURL_mutable_toobig",
657 "413 Request Entity Too Large",
658 "SDMF is limited to one segment, and 10001 > 10000",
660 self.public_url + "/foo/new.txt?mutable=true",
661 "b" * (self.s.MUTABLE_SIZELIMIT+1))
664 def test_PUT_NEWFILEURL_replace(self):
665 d = self.PUT(self.public_url + "/foo/bar.txt", self.NEWFILE_CONTENTS)
666 # TODO: we lose the response code, so we can't check this
667 #self.failUnlessEqual(responsecode, 200)
668 d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"bar.txt")
669 d.addCallback(lambda res:
670 self.failUnlessChildContentsAre(self._foo_node, u"bar.txt",
671 self.NEWFILE_CONTENTS))
674 def test_PUT_NEWFILEURL_bad_t(self):
675 d = self.shouldFail2(error.Error, "PUT_bad_t", "400 Bad Request",
676 "PUT to a file: bad t=bogus",
677 self.PUT, self.public_url + "/foo/bar.txt?t=bogus",
681 def test_PUT_NEWFILEURL_no_replace(self):
682 d = self.PUT(self.public_url + "/foo/bar.txt?replace=false",
683 self.NEWFILE_CONTENTS)
684 d.addBoth(self.shouldFail, error.Error, "PUT_NEWFILEURL_no_replace",
686 "There was already a child by that name, and you asked me "
690 def test_PUT_NEWFILEURL_mkdirs(self):
691 d = self.PUT(self.public_url + "/foo/newdir/new.txt", self.NEWFILE_CONTENTS)
693 d.addCallback(self.failUnlessURIMatchesChild, fn, u"newdir/new.txt")
694 d.addCallback(lambda res: self.failIfNodeHasChild(fn, u"new.txt"))
695 d.addCallback(lambda res: self.failUnlessNodeHasChild(fn, u"newdir"))
696 d.addCallback(lambda res:
697 self.failUnlessChildContentsAre(fn, u"newdir/new.txt",
698 self.NEWFILE_CONTENTS))
701 def test_PUT_NEWFILEURL_blocked(self):
702 d = self.PUT(self.public_url + "/foo/blockingfile/new.txt",
703 self.NEWFILE_CONTENTS)
704 d.addBoth(self.shouldFail, error.Error, "PUT_NEWFILEURL_blocked",
706 "Unable to create directory 'blockingfile': a file was in the way")
709 def test_DELETE_FILEURL(self):
710 d = self.DELETE(self.public_url + "/foo/bar.txt")
711 d.addCallback(lambda res:
712 self.failIfNodeHasChild(self._foo_node, u"bar.txt"))
715 def test_DELETE_FILEURL_missing(self):
716 d = self.DELETE(self.public_url + "/foo/missing")
717 d.addBoth(self.should404, "test_DELETE_FILEURL_missing")
720 def test_DELETE_FILEURL_missing2(self):
721 d = self.DELETE(self.public_url + "/missing/missing")
722 d.addBoth(self.should404, "test_DELETE_FILEURL_missing2")
725 def test_GET_FILEURL_json(self):
726 # twisted.web.http.parse_qs ignores any query args without an '=', so
727 # I can't do "GET /path?json", I have to do "GET /path/t=json"
728 # instead. This may make it tricky to emulate the S3 interface
730 d = self.GET(self.public_url + "/foo/bar.txt?t=json")
731 d.addCallback(self.failUnlessIsBarJSON)
734 def test_GET_FILEURL_json_missing(self):
735 d = self.GET(self.public_url + "/foo/missing?json")
736 d.addBoth(self.should404, "test_GET_FILEURL_json_missing")
739 def test_GET_FILEURL_uri(self):
740 d = self.GET(self.public_url + "/foo/bar.txt?t=uri")
742 self.failUnlessEqual(res, self._bar_txt_uri)
743 d.addCallback(_check)
744 d.addCallback(lambda res:
745 self.GET(self.public_url + "/foo/bar.txt?t=readonly-uri"))
747 # for now, for files, uris and readonly-uris are the same
748 self.failUnlessEqual(res, self._bar_txt_uri)
749 d.addCallback(_check2)
752 def test_GET_FILEURL_badtype(self):
753 d = self.shouldHTTPError2("GET t=bogus", 400, "Bad Request",
756 self.public_url + "/foo/bar.txt?t=bogus")
759 def test_GET_FILEURL_uri_missing(self):
760 d = self.GET(self.public_url + "/foo/missing?t=uri")
761 d.addBoth(self.should404, "test_GET_FILEURL_uri_missing")
764 def test_GET_DIRURL(self):
765 # the addSlash means we get a redirect here
766 # from /uri/$URI/foo/ , we need ../../../ to get back to the root
768 d = self.GET(self.public_url + "/foo", followRedirect=True)
770 self.failUnless(('<a href="%s">Return to Welcome page' % ROOT)
772 # the FILE reference points to a URI, but it should end in bar.txt
773 bar_url = ("%s/file/%s/@@named=/bar.txt" %
774 (ROOT, urllib.quote(self._bar_txt_uri)))
775 get_bar = "".join([r'<td>',
776 r'<a href="%s">bar.txt</a>' % bar_url,
779 r'\s+<td>%d</td>' % len(self.BAR_CONTENTS),
781 self.failUnless(re.search(get_bar, res), res)
782 for line in res.split("\n"):
783 # find the line that contains the delete button for bar.txt
784 if ("form action" in line and
785 'value="delete"' in line and
786 'value="bar.txt"' in line):
787 # the form target should use a relative URL
788 foo_url = urllib.quote("%s/uri/%s/" % (ROOT, self._foo_uri))
789 self.failUnless(('action="%s"' % foo_url) in line, line)
790 # and the when_done= should too
791 #done_url = urllib.quote(???)
792 #self.failUnless(('name="when_done" value="%s"' % done_url)
796 self.fail("unable to find delete-bar.txt line", res)
798 # the DIR reference just points to a URI
799 sub_url = ("%s/uri/%s/" % (ROOT, urllib.quote(self._sub_uri)))
800 get_sub = ((r'<td><a href="%s">sub</a></td>' % sub_url)
801 + r'\s+<td>DIR</td>')
802 self.failUnless(re.search(get_sub, res), res)
803 d.addCallback(_check)
805 # look at a directory which is readonly
806 d.addCallback(lambda res:
807 self.GET(self.public_url + "/reedownlee", followRedirect=True))
809 self.failUnless("(readonly)" in res, res)
810 self.failIf("Upload a file" in res, res)
811 d.addCallback(_check2)
813 # and at a directory that contains a readonly directory
814 d.addCallback(lambda res:
815 self.GET(self.public_url, followRedirect=True))
817 self.failUnless(re.search(r'<td><a href="[\.\/]+/uri/URI%3ADIR2-RO%3A[^"]+">reedownlee</a>'
818 '</td>\s+<td>DIR-RO</td>', res))
819 d.addCallback(_check3)
823 def test_GET_DIRURL_badtype(self):
824 d = self.shouldHTTPError2("test_GET_DIRURL_badtype",
828 self.public_url + "/foo?t=bogus")
831 def test_GET_DIRURL_json(self):
832 d = self.GET(self.public_url + "/foo?t=json")
833 d.addCallback(self.failUnlessIsFooJSON)
837 def test_POST_DIRURL_manifest_no_ophandle(self):
838 d = self.shouldFail2(error.Error,
839 "test_POST_DIRURL_manifest_no_ophandle",
841 "slow operation requires ophandle=",
842 self.POST, self.public_url, t="start-manifest")
845 def test_POST_DIRURL_manifest(self):
846 d = defer.succeed(None)
847 def getman(ignored, output):
848 d = self.POST(self.public_url + "/foo/?t=start-manifest&ophandle=125",
850 d.addCallback(self.wait_for_operation, "125")
851 d.addCallback(self.get_operation_results, "125", output)
853 d.addCallback(getman, None)
854 def _got_html(manifest):
855 self.failUnless("Manifest of SI=" in manifest)
856 self.failUnless("<td>sub</td>" in manifest)
857 self.failUnless(self._sub_uri in manifest)
858 self.failUnless("<td>sub/baz.txt</td>" in manifest)
859 d.addCallback(_got_html)
861 # both t=status and unadorned GET should be identical
862 d.addCallback(lambda res: self.GET("/operations/125"))
863 d.addCallback(_got_html)
865 d.addCallback(getman, "html")
866 d.addCallback(_got_html)
867 d.addCallback(getman, "text")
868 def _got_text(manifest):
869 self.failUnless("\nsub " + self._sub_uri + "\n" in manifest)
870 self.failUnless("\nsub/baz.txt URI:CHK:" in manifest)
871 d.addCallback(_got_text)
872 d.addCallback(getman, "JSON")
873 def _got_json(manifest):
874 data = manifest["manifest"]
876 for (path_list, cap) in data:
877 got[tuple(path_list)] = cap
878 self.failUnlessEqual(got[(u"sub",)], self._sub_uri)
879 self.failUnless((u"sub",u"baz.txt") in got)
880 d.addCallback(_got_json)
883 def test_POST_DIRURL_deepsize_no_ophandle(self):
884 d = self.shouldFail2(error.Error,
885 "test_POST_DIRURL_deepsize_no_ophandle",
887 "slow operation requires ophandle=",
888 self.POST, self.public_url, t="start-deep-size")
891 def test_POST_DIRURL_deepsize(self):
892 d = self.POST(self.public_url + "/foo/?t=start-deep-size&ophandle=126",
894 d.addCallback(self.wait_for_operation, "126")
895 d.addCallback(self.get_operation_results, "126", "json")
897 self.failUnlessEqual(data["finished"], True)
899 self.failUnless(size > 1000)
900 d.addCallback(_got_json)
901 d.addCallback(self.get_operation_results, "126", "text")
903 mo = re.search(r'^size: (\d+)$', res, re.M)
904 self.failUnless(mo, res)
905 size = int(mo.group(1))
906 # with directories, the size varies.
907 self.failUnless(size > 1000)
908 d.addCallback(_got_text)
911 def test_POST_DIRURL_deepstats_no_ophandle(self):
912 d = self.shouldFail2(error.Error,
913 "test_POST_DIRURL_deepstats_no_ophandle",
915 "slow operation requires ophandle=",
916 self.POST, self.public_url, t="start-deep-stats")
919 def test_POST_DIRURL_deepstats(self):
920 d = self.POST(self.public_url + "/foo/?t=start-deep-stats&ophandle=127",
922 d.addCallback(self.wait_for_operation, "127")
923 d.addCallback(self.get_operation_results, "127", "json")
924 def _got_json(stats):
925 expected = {"count-immutable-files": 3,
926 "count-mutable-files": 0,
927 "count-literal-files": 0,
929 "count-directories": 3,
930 "size-immutable-files": 57,
931 "size-literal-files": 0,
932 #"size-directories": 1912, # varies
933 #"largest-directory": 1590,
934 "largest-directory-children": 5,
935 "largest-immutable-file": 19,
937 for k,v in expected.iteritems():
938 self.failUnlessEqual(stats[k], v,
939 "stats[%s] was %s, not %s" %
941 self.failUnlessEqual(stats["size-files-histogram"],
943 d.addCallback(_got_json)
946 def test_GET_DIRURL_uri(self):
947 d = self.GET(self.public_url + "/foo?t=uri")
949 self.failUnlessEqual(res, self._foo_uri)
950 d.addCallback(_check)
953 def test_GET_DIRURL_readonly_uri(self):
954 d = self.GET(self.public_url + "/foo?t=readonly-uri")
956 self.failUnlessEqual(res, self._foo_readonly_uri)
957 d.addCallback(_check)
960 def test_PUT_NEWDIRURL(self):
961 d = self.PUT(self.public_url + "/foo/newdir?t=mkdir", "")
962 d.addCallback(lambda res:
963 self.failUnlessNodeHasChild(self._foo_node, u"newdir"))
964 d.addCallback(lambda res: self._foo_node.get(u"newdir"))
965 d.addCallback(self.failUnlessNodeKeysAre, [])
968 def test_PUT_NEWDIRURL_exists(self):
969 d = self.PUT(self.public_url + "/foo/sub?t=mkdir", "")
970 d.addCallback(lambda res:
971 self.failUnlessNodeHasChild(self._foo_node, u"sub"))
972 d.addCallback(lambda res: self._foo_node.get(u"sub"))
973 d.addCallback(self.failUnlessNodeKeysAre, [u"baz.txt"])
976 def test_PUT_NEWDIRURL_blocked(self):
977 d = self.shouldFail2(error.Error, "PUT_NEWDIRURL_blocked",
978 "409 Conflict", "Unable to create directory 'bar.txt': a file was in the way",
980 self.public_url + "/foo/bar.txt/sub?t=mkdir", "")
981 d.addCallback(lambda res:
982 self.failUnlessNodeHasChild(self._foo_node, u"sub"))
983 d.addCallback(lambda res: self._foo_node.get(u"sub"))
984 d.addCallback(self.failUnlessNodeKeysAre, [u"baz.txt"])
987 def test_PUT_NEWDIRURL_mkdir_p(self):
988 d = defer.succeed(None)
989 d.addCallback(lambda res: self.POST(self.public_url + "/foo", t='mkdir', name='mkp'))
990 d.addCallback(lambda res: self.failUnlessNodeHasChild(self._foo_node, u"mkp"))
991 d.addCallback(lambda res: self._foo_node.get(u"mkp"))
992 def mkdir_p(mkpnode):
993 url = '/uri/%s?t=mkdir-p&path=/sub1/sub2' % urllib.quote(mkpnode.get_uri())
995 def made_subsub(ssuri):
996 d = self._foo_node.get_child_at_path(u"mkp/sub1/sub2")
997 d.addCallback(lambda ssnode: self.failUnlessEqual(ssnode.get_uri(), ssuri))
999 d.addCallback(lambda uri2: self.failUnlessEqual(uri2, ssuri))
1001 d.addCallback(made_subsub)
1003 d.addCallback(mkdir_p)
1006 def test_PUT_NEWDIRURL_mkdirs(self):
1007 d = self.PUT(self.public_url + "/foo/subdir/newdir?t=mkdir", "")
1008 d.addCallback(lambda res:
1009 self.failIfNodeHasChild(self._foo_node, u"newdir"))
1010 d.addCallback(lambda res:
1011 self.failUnlessNodeHasChild(self._foo_node, u"subdir"))
1012 d.addCallback(lambda res:
1013 self._foo_node.get_child_at_path(u"subdir/newdir"))
1014 d.addCallback(self.failUnlessNodeKeysAre, [])
1017 def test_DELETE_DIRURL(self):
1018 d = self.DELETE(self.public_url + "/foo")
1019 d.addCallback(lambda res:
1020 self.failIfNodeHasChild(self.public_root, u"foo"))
1023 def test_DELETE_DIRURL_missing(self):
1024 d = self.DELETE(self.public_url + "/foo/missing")
1025 d.addBoth(self.should404, "test_DELETE_DIRURL_missing")
1026 d.addCallback(lambda res:
1027 self.failUnlessNodeHasChild(self.public_root, u"foo"))
1030 def test_DELETE_DIRURL_missing2(self):
1031 d = self.DELETE(self.public_url + "/missing")
1032 d.addBoth(self.should404, "test_DELETE_DIRURL_missing2")
1035 def dump_root(self):
1037 w = webish.DirnodeWalkerMixin()
1038 def visitor(childpath, childnode, metadata):
1040 d = w.walk(self.public_root, visitor)
1043 def failUnlessNodeKeysAre(self, node, expected_keys):
1044 for k in expected_keys:
1045 assert isinstance(k, unicode)
1047 def _check(children):
1048 self.failUnlessEqual(sorted(children.keys()), sorted(expected_keys))
1049 d.addCallback(_check)
1051 def failUnlessNodeHasChild(self, node, name):
1052 assert isinstance(name, unicode)
1054 def _check(children):
1055 self.failUnless(name in children)
1056 d.addCallback(_check)
1058 def failIfNodeHasChild(self, node, name):
1059 assert isinstance(name, unicode)
1061 def _check(children):
1062 self.failIf(name in children)
1063 d.addCallback(_check)
1066 def failUnlessChildContentsAre(self, node, name, expected_contents):
1067 assert isinstance(name, unicode)
1068 d = node.get_child_at_path(name)
1069 d.addCallback(lambda node: node.download_to_data())
1070 def _check(contents):
1071 self.failUnlessEqual(contents, expected_contents)
1072 d.addCallback(_check)
1075 def failUnlessMutableChildContentsAre(self, node, name, expected_contents):
1076 assert isinstance(name, unicode)
1077 d = node.get_child_at_path(name)
1078 d.addCallback(lambda node: node.download_best_version())
1079 def _check(contents):
1080 self.failUnlessEqual(contents, expected_contents)
1081 d.addCallback(_check)
1084 def failUnlessChildURIIs(self, node, name, expected_uri):
1085 assert isinstance(name, unicode)
1086 d = node.get_child_at_path(name)
1088 self.failUnlessEqual(child.get_uri(), expected_uri.strip())
1089 d.addCallback(_check)
1092 def failUnlessURIMatchesChild(self, got_uri, node, name):
1093 assert isinstance(name, unicode)
1094 d = node.get_child_at_path(name)
1096 self.failUnlessEqual(got_uri.strip(), child.get_uri())
1097 d.addCallback(_check)
1100 def failUnlessCHKURIHasContents(self, got_uri, contents):
1101 self.failUnless(FakeCHKFileNode.all_contents[got_uri] == contents)
1103 def test_POST_upload(self):
1104 d = self.POST(self.public_url + "/foo", t="upload",
1105 file=("new.txt", self.NEWFILE_CONTENTS))
1107 d.addCallback(self.failUnlessURIMatchesChild, fn, u"new.txt")
1108 d.addCallback(lambda res:
1109 self.failUnlessChildContentsAre(fn, u"new.txt",
1110 self.NEWFILE_CONTENTS))
1113 def test_POST_upload_unicode(self):
1114 filename = u"n\u00e9wer.txt" # n e-acute w e r . t x t
1115 d = self.POST(self.public_url + "/foo", t="upload",
1116 file=(filename, self.NEWFILE_CONTENTS))
1118 d.addCallback(self.failUnlessURIMatchesChild, fn, filename)
1119 d.addCallback(lambda res:
1120 self.failUnlessChildContentsAre(fn, filename,
1121 self.NEWFILE_CONTENTS))
1122 target_url = self.public_url + "/foo/" + filename.encode("utf-8")
1123 d.addCallback(lambda res: self.GET(target_url))
1124 d.addCallback(lambda contents: self.failUnlessEqual(contents,
1125 self.NEWFILE_CONTENTS,
1129 def test_POST_upload_unicode_named(self):
1130 filename = u"n\u00e9wer.txt" # n e-acute w e r . t x t
1131 d = self.POST(self.public_url + "/foo", t="upload",
1133 file=("overridden", self.NEWFILE_CONTENTS))
1135 d.addCallback(self.failUnlessURIMatchesChild, fn, filename)
1136 d.addCallback(lambda res:
1137 self.failUnlessChildContentsAre(fn, filename,
1138 self.NEWFILE_CONTENTS))
1139 target_url = self.public_url + "/foo/" + filename.encode("utf-8")
1140 d.addCallback(lambda res: self.GET(target_url))
1141 d.addCallback(lambda contents: self.failUnlessEqual(contents,
1142 self.NEWFILE_CONTENTS,
1146 def test_POST_upload_no_link(self):
1147 d = self.POST("/uri", t="upload",
1148 file=("new.txt", self.NEWFILE_CONTENTS))
1149 def _check_upload_results(page):
1150 # this should be a page which describes the results of the upload
1151 # that just finished.
1152 self.failUnless("Upload Results:" in page)
1153 self.failUnless("URI:" in page)
1154 uri_re = re.compile("URI: <tt><span>(.*)</span>")
1155 mo = uri_re.search(page)
1156 self.failUnless(mo, page)
1157 new_uri = mo.group(1)
1159 d.addCallback(_check_upload_results)
1160 d.addCallback(self.failUnlessCHKURIHasContents, self.NEWFILE_CONTENTS)
1163 def test_POST_upload_no_link_whendone(self):
1164 d = self.POST("/uri", t="upload", when_done="/",
1165 file=("new.txt", self.NEWFILE_CONTENTS))
1166 d.addBoth(self.shouldRedirect, "/")
1169 def shouldRedirect2(self, which, checker, callable, *args, **kwargs):
1170 d = defer.maybeDeferred(callable, *args, **kwargs)
1172 if isinstance(res, failure.Failure):
1173 res.trap(error.PageRedirect)
1174 statuscode = res.value.status
1175 target = res.value.location
1176 return checker(statuscode, target)
1177 self.fail("%s: callable was supposed to redirect, not return '%s'"
1182 def test_POST_upload_no_link_whendone_results(self):
1183 def check(statuscode, target):
1184 self.failUnlessEqual(statuscode, str(http.FOUND))
1185 self.failUnless(target.startswith(self.webish_url), target)
1186 return client.getPage(target, method="GET")
1187 d = self.shouldRedirect2("test_POST_upload_no_link_whendone_results",
1189 self.POST, "/uri", t="upload",
1190 when_done="/uri/%(uri)s",
1191 file=("new.txt", self.NEWFILE_CONTENTS))
1192 d.addCallback(lambda res:
1193 self.failUnlessEqual(res, self.NEWFILE_CONTENTS))
1196 def test_POST_upload_no_link_mutable(self):
1197 d = self.POST("/uri", t="upload", mutable="true",
1198 file=("new.txt", self.NEWFILE_CONTENTS))
1199 def _check(new_uri):
1200 new_uri = new_uri.strip()
1201 self.new_uri = new_uri
1203 self.failUnless(IMutableFileURI.providedBy(u))
1204 self.failUnless(u.storage_index in FakeMutableFileNode.all_contents)
1205 n = self.s.create_node_from_uri(new_uri)
1206 return n.download_best_version()
1207 d.addCallback(_check)
1209 self.failUnlessEqual(data, self.NEWFILE_CONTENTS)
1210 return self.GET("/uri/%s" % urllib.quote(self.new_uri))
1211 d.addCallback(_check2)
1213 self.failUnlessEqual(data, self.NEWFILE_CONTENTS)
1214 return self.GET("/file/%s" % urllib.quote(self.new_uri))
1215 d.addCallback(_check3)
1217 self.failUnlessEqual(data, self.NEWFILE_CONTENTS)
1218 d.addCallback(_check4)
1221 def test_POST_upload_no_link_mutable_toobig(self):
1222 d = self.shouldFail2(error.Error,
1223 "test_POST_upload_no_link_mutable_toobig",
1224 "413 Request Entity Too Large",
1225 "SDMF is limited to one segment, and 10001 > 10000",
1227 "/uri", t="upload", mutable="true",
1229 "b" * (self.s.MUTABLE_SIZELIMIT+1)) )
1232 def test_POST_upload_mutable(self):
1233 # this creates a mutable file
1234 d = self.POST(self.public_url + "/foo", t="upload", mutable="true",
1235 file=("new.txt", self.NEWFILE_CONTENTS))
1237 d.addCallback(self.failUnlessURIMatchesChild, fn, u"new.txt")
1238 d.addCallback(lambda res:
1239 self.failUnlessMutableChildContentsAre(fn, u"new.txt",
1240 self.NEWFILE_CONTENTS))
1241 d.addCallback(lambda res: self._foo_node.get(u"new.txt"))
1243 self.failUnless(IMutableFileNode.providedBy(newnode))
1244 self.failUnless(newnode.is_mutable())
1245 self.failIf(newnode.is_readonly())
1246 self._mutable_node = newnode
1247 self._mutable_uri = newnode.get_uri()
1250 # now upload it again and make sure that the URI doesn't change
1251 NEWER_CONTENTS = self.NEWFILE_CONTENTS + "newer\n"
1252 d.addCallback(lambda res:
1253 self.POST(self.public_url + "/foo", t="upload",
1255 file=("new.txt", NEWER_CONTENTS)))
1256 d.addCallback(self.failUnlessURIMatchesChild, fn, u"new.txt")
1257 d.addCallback(lambda res:
1258 self.failUnlessMutableChildContentsAre(fn, u"new.txt",
1260 d.addCallback(lambda res: self._foo_node.get(u"new.txt"))
1262 self.failUnless(IMutableFileNode.providedBy(newnode))
1263 self.failUnless(newnode.is_mutable())
1264 self.failIf(newnode.is_readonly())
1265 self.failUnlessEqual(self._mutable_uri, newnode.get_uri())
1266 d.addCallback(_got2)
1268 # upload a second time, using PUT instead of POST
1269 NEW2_CONTENTS = NEWER_CONTENTS + "overwrite with PUT\n"
1270 d.addCallback(lambda res:
1271 self.PUT(self.public_url + "/foo/new.txt", NEW2_CONTENTS))
1272 d.addCallback(self.failUnlessURIMatchesChild, fn, u"new.txt")
1273 d.addCallback(lambda res:
1274 self.failUnlessMutableChildContentsAre(fn, u"new.txt",
1277 # finally list the directory, since mutable files are displayed
1278 # slightly differently
1280 d.addCallback(lambda res:
1281 self.GET(self.public_url + "/foo/",
1282 followRedirect=True))
1283 def _check_page(res):
1284 # TODO: assert more about the contents
1285 self.failUnless("SSK" in res)
1287 d.addCallback(_check_page)
1289 d.addCallback(lambda res: self._foo_node.get(u"new.txt"))
1291 self.failUnless(IMutableFileNode.providedBy(newnode))
1292 self.failUnless(newnode.is_mutable())
1293 self.failIf(newnode.is_readonly())
1294 self.failUnlessEqual(self._mutable_uri, newnode.get_uri())
1295 d.addCallback(_got3)
1297 # look at the JSON form of the enclosing directory
1298 d.addCallback(lambda res:
1299 self.GET(self.public_url + "/foo/?t=json",
1300 followRedirect=True))
1301 def _check_page_json(res):
1302 parsed = simplejson.loads(res)
1303 self.failUnlessEqual(parsed[0], "dirnode")
1304 children = dict( [(unicode(name),value)
1306 in parsed[1]["children"].iteritems()] )
1307 self.failUnless("new.txt" in children)
1308 new_json = children["new.txt"]
1309 self.failUnlessEqual(new_json[0], "filenode")
1310 self.failUnless(new_json[1]["mutable"])
1311 self.failUnlessEqual(new_json[1]["rw_uri"], self._mutable_uri)
1312 ro_uri = unicode(self._mutable_node.get_readonly().to_string())
1313 self.failUnlessEqual(new_json[1]["ro_uri"], ro_uri)
1314 d.addCallback(_check_page_json)
1316 # and the JSON form of the file
1317 d.addCallback(lambda res:
1318 self.GET(self.public_url + "/foo/new.txt?t=json"))
1319 def _check_file_json(res):
1320 parsed = simplejson.loads(res)
1321 self.failUnlessEqual(parsed[0], "filenode")
1322 self.failUnless(parsed[1]["mutable"])
1323 self.failUnlessEqual(parsed[1]["rw_uri"], self._mutable_uri)
1324 ro_uri = unicode(self._mutable_node.get_readonly().to_string())
1325 self.failUnlessEqual(parsed[1]["ro_uri"], ro_uri)
1326 d.addCallback(_check_file_json)
1328 # and look at t=uri and t=readonly-uri
1329 d.addCallback(lambda res:
1330 self.GET(self.public_url + "/foo/new.txt?t=uri"))
1331 d.addCallback(lambda res: self.failUnlessEqual(res, self._mutable_uri))
1332 d.addCallback(lambda res:
1333 self.GET(self.public_url + "/foo/new.txt?t=readonly-uri"))
1334 def _check_ro_uri(res):
1335 ro_uri = unicode(self._mutable_node.get_readonly().to_string())
1336 self.failUnlessEqual(res, ro_uri)
1337 d.addCallback(_check_ro_uri)
1339 # make sure we can get to it from /uri/URI
1340 d.addCallback(lambda res:
1341 self.GET("/uri/%s" % urllib.quote(self._mutable_uri)))
1342 d.addCallback(lambda res:
1343 self.failUnlessEqual(res, NEW2_CONTENTS))
1345 # and that HEAD computes the size correctly
1346 d.addCallback(lambda res:
1347 self.HEAD(self.public_url + "/foo/new.txt"))
1348 def _got_headers(headers):
1349 self.failUnlessEqual(headers["content-length"][0],
1350 str(len(NEW2_CONTENTS)))
1351 self.failUnlessEqual(headers["content-type"], ["text/plain"])
1352 d.addCallback(_got_headers)
1354 # make sure that size errors are displayed correctly for overwrite
1355 d.addCallback(lambda res:
1356 self.shouldFail2(error.Error,
1357 "test_POST_upload_mutable-toobig",
1358 "413 Request Entity Too Large",
1359 "SDMF is limited to one segment, and 10001 > 10000",
1361 self.public_url + "/foo", t="upload",
1364 "b" * (self.s.MUTABLE_SIZELIMIT+1)),
1367 d.addErrback(self.dump_error)
1370 def test_POST_upload_mutable_toobig(self):
1371 d = self.shouldFail2(error.Error,
1372 "test_POST_upload_no_link_mutable_toobig",
1373 "413 Request Entity Too Large",
1374 "SDMF is limited to one segment, and 10001 > 10000",
1376 self.public_url + "/foo",
1377 t="upload", mutable="true",
1379 "b" * (self.s.MUTABLE_SIZELIMIT+1)) )
1382 def dump_error(self, f):
1383 # if the web server returns an error code (like 400 Bad Request),
1384 # web.client.getPage puts the HTTP response body into the .response
1385 # attribute of the exception object that it gives back. It does not
1386 # appear in the Failure's repr(), so the ERROR that trial displays
1387 # will be rather terse and unhelpful. addErrback this method to the
1388 # end of your chain to get more information out of these errors.
1389 if f.check(error.Error):
1390 print "web.error.Error:"
1392 print f.value.response
1395 def test_POST_upload_replace(self):
1396 d = self.POST(self.public_url + "/foo", t="upload",
1397 file=("bar.txt", self.NEWFILE_CONTENTS))
1399 d.addCallback(self.failUnlessURIMatchesChild, fn, u"bar.txt")
1400 d.addCallback(lambda res:
1401 self.failUnlessChildContentsAre(fn, u"bar.txt",
1402 self.NEWFILE_CONTENTS))
1405 def test_POST_upload_no_replace_ok(self):
1406 d = self.POST(self.public_url + "/foo?replace=false", t="upload",
1407 file=("new.txt", self.NEWFILE_CONTENTS))
1408 d.addCallback(lambda res: self.GET(self.public_url + "/foo/new.txt"))
1409 d.addCallback(lambda res: self.failUnlessEqual(res,
1410 self.NEWFILE_CONTENTS))
1413 def test_POST_upload_no_replace_queryarg(self):
1414 d = self.POST(self.public_url + "/foo?replace=false", t="upload",
1415 file=("bar.txt", self.NEWFILE_CONTENTS))
1416 d.addBoth(self.shouldFail, error.Error,
1417 "POST_upload_no_replace_queryarg",
1419 "There was already a child by that name, and you asked me "
1420 "to not replace it")
1421 d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt"))
1422 d.addCallback(self.failUnlessIsBarDotTxt)
1425 def test_POST_upload_no_replace_field(self):
1426 d = self.POST(self.public_url + "/foo", t="upload", replace="false",
1427 file=("bar.txt", self.NEWFILE_CONTENTS))
1428 d.addBoth(self.shouldFail, error.Error, "POST_upload_no_replace_field",
1430 "There was already a child by that name, and you asked me "
1431 "to not replace it")
1432 d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt"))
1433 d.addCallback(self.failUnlessIsBarDotTxt)
1436 def test_POST_upload_whendone(self):
1437 d = self.POST(self.public_url + "/foo", t="upload", when_done="/THERE",
1438 file=("new.txt", self.NEWFILE_CONTENTS))
1439 d.addBoth(self.shouldRedirect, "/THERE")
1441 d.addCallback(lambda res:
1442 self.failUnlessChildContentsAre(fn, u"new.txt",
1443 self.NEWFILE_CONTENTS))
1446 def test_POST_upload_named(self):
1448 d = self.POST(self.public_url + "/foo", t="upload",
1449 name="new.txt", file=self.NEWFILE_CONTENTS)
1450 d.addCallback(self.failUnlessURIMatchesChild, fn, u"new.txt")
1451 d.addCallback(lambda res:
1452 self.failUnlessChildContentsAre(fn, u"new.txt",
1453 self.NEWFILE_CONTENTS))
1456 def test_POST_upload_named_badfilename(self):
1457 d = self.POST(self.public_url + "/foo", t="upload",
1458 name="slashes/are/bad.txt", file=self.NEWFILE_CONTENTS)
1459 d.addBoth(self.shouldFail, error.Error,
1460 "test_POST_upload_named_badfilename",
1462 "name= may not contain a slash",
1464 # make sure that nothing was added
1465 d.addCallback(lambda res:
1466 self.failUnlessNodeKeysAre(self._foo_node,
1467 [u"bar.txt", u"blockingfile",
1468 u"empty", u"n\u00fc.txt",
1472 def test_POST_FILEURL_check(self):
1473 bar_url = self.public_url + "/foo/bar.txt"
1474 d = self.POST(bar_url, t="check")
1476 self.failUnless("Healthy!" in res)
1477 d.addCallback(_check)
1478 redir_url = "http://allmydata.org/TARGET"
1479 def _check2(statuscode, target):
1480 self.failUnlessEqual(statuscode, str(http.FOUND))
1481 self.failUnlessEqual(target, redir_url)
1482 d.addCallback(lambda res:
1483 self.shouldRedirect2("test_POST_FILEURL_check",
1487 when_done=redir_url))
1488 d.addCallback(lambda res:
1489 self.POST(bar_url, t="check", return_to=redir_url))
1491 self.failUnless("Healthy!" in res)
1492 self.failUnless("Return to parent directory" in res)
1493 self.failUnless(redir_url in res)
1494 d.addCallback(_check3)
1496 d.addCallback(lambda res:
1497 self.POST(bar_url, t="check", output="JSON"))
1498 def _check_json(res):
1499 data = simplejson.loads(res)
1500 self.failUnless("storage-index" in data)
1501 self.failUnless(data["results"]["healthy"])
1502 d.addCallback(_check_json)
1506 def test_POST_FILEURL_check_and_repair(self):
1507 bar_url = self.public_url + "/foo/bar.txt"
1508 d = self.POST(bar_url, t="check", repair="true")
1510 self.failUnless("Healthy!" in res)
1511 d.addCallback(_check)
1512 redir_url = "http://allmydata.org/TARGET"
1513 def _check2(statuscode, target):
1514 self.failUnlessEqual(statuscode, str(http.FOUND))
1515 self.failUnlessEqual(target, redir_url)
1516 d.addCallback(lambda res:
1517 self.shouldRedirect2("test_POST_FILEURL_check_and_repair",
1520 t="check", repair="true",
1521 when_done=redir_url))
1522 d.addCallback(lambda res:
1523 self.POST(bar_url, t="check", return_to=redir_url))
1525 self.failUnless("Healthy!" in res)
1526 self.failUnless("Return to parent directory" in res)
1527 self.failUnless(redir_url in res)
1528 d.addCallback(_check3)
1531 def test_POST_DIRURL_check(self):
1532 foo_url = self.public_url + "/foo/"
1533 d = self.POST(foo_url, t="check")
1535 self.failUnless("Healthy!" in res)
1536 d.addCallback(_check)
1537 redir_url = "http://allmydata.org/TARGET"
1538 def _check2(statuscode, target):
1539 self.failUnlessEqual(statuscode, str(http.FOUND))
1540 self.failUnlessEqual(target, redir_url)
1541 d.addCallback(lambda res:
1542 self.shouldRedirect2("test_POST_DIRURL_check",
1546 when_done=redir_url))
1547 d.addCallback(lambda res:
1548 self.POST(foo_url, t="check", return_to=redir_url))
1550 self.failUnless("Healthy!" in res)
1551 self.failUnless("Return to parent directory" in res)
1552 self.failUnless(redir_url in res)
1553 d.addCallback(_check3)
1555 d.addCallback(lambda res:
1556 self.POST(foo_url, t="check", output="JSON"))
1557 def _check_json(res):
1558 data = simplejson.loads(res)
1559 self.failUnless("storage-index" in data)
1560 self.failUnless(data["results"]["healthy"])
1561 d.addCallback(_check_json)
1565 def test_POST_DIRURL_check_and_repair(self):
1566 foo_url = self.public_url + "/foo/"
1567 d = self.POST(foo_url, t="check", repair="true")
1569 self.failUnless("Healthy!" 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_and_repair",
1579 t="check", repair="true",
1580 when_done=redir_url))
1581 d.addCallback(lambda res:
1582 self.POST(foo_url, t="check", return_to=redir_url))
1584 self.failUnless("Healthy!" in res)
1585 self.failUnless("Return to parent directory" in res)
1586 self.failUnless(redir_url in res)
1587 d.addCallback(_check3)
1590 def wait_for_operation(self, ignored, ophandle):
1591 url = "/operations/" + ophandle
1592 url += "?t=status&output=JSON"
1595 data = simplejson.loads(res)
1596 if not data["finished"]:
1597 d = self.stall(delay=1.0)
1598 d.addCallback(self.wait_for_operation, ophandle)
1604 def get_operation_results(self, ignored, ophandle, output=None):
1605 url = "/operations/" + ophandle
1608 url += "&output=" + output
1611 if output and output.lower() == "json":
1612 return simplejson.loads(res)
1617 def test_POST_DIRURL_deepcheck_no_ophandle(self):
1618 d = self.shouldFail2(error.Error,
1619 "test_POST_DIRURL_deepcheck_no_ophandle",
1621 "slow operation requires ophandle=",
1622 self.POST, self.public_url, t="start-deep-check")
1625 def test_POST_DIRURL_deepcheck(self):
1626 def _check_redirect(statuscode, target):
1627 self.failUnlessEqual(statuscode, str(http.FOUND))
1628 self.failUnless(target.endswith("/operations/123"))
1629 d = self.shouldRedirect2("test_POST_DIRURL_deepcheck", _check_redirect,
1630 self.POST, self.public_url,
1631 t="start-deep-check", ophandle="123")
1632 d.addCallback(self.wait_for_operation, "123")
1633 def _check_json(data):
1634 self.failUnlessEqual(data["finished"], True)
1635 self.failUnlessEqual(data["count-objects-checked"], 8)
1636 self.failUnlessEqual(data["count-objects-healthy"], 8)
1637 d.addCallback(_check_json)
1638 d.addCallback(self.get_operation_results, "123", "html")
1639 def _check_html(res):
1640 self.failUnless("Objects Checked: <span>8</span>" in res)
1641 self.failUnless("Objects Healthy: <span>8</span>" in res)
1642 d.addCallback(_check_html)
1643 d.addCallback(lambda res:
1644 self.shouldFail2(error.Error, "one", "404 Not Found",
1645 "No detailed results for SI bogus",
1646 self.GET, "/operations/123/bogus"))
1647 foo_si = self._foo_node.get_storage_index()
1648 foo_si_s = base32.b2a(foo_si)
1649 d.addCallback(lambda res:
1650 self.GET("/operations/123/%s?output=JSON" % foo_si_s))
1651 def _check_foo_json(res):
1652 data = simplejson.loads(res)
1653 self.failUnlessEqual(data["storage-index"], foo_si_s)
1654 self.failUnless(data["results"]["healthy"])
1655 d.addCallback(_check_foo_json)
1658 def test_POST_DIRURL_deepcheck_and_repair(self):
1659 d = self.POST(self.public_url, t="start-deep-check", repair="true",
1660 ophandle="124", output="json", followRedirect=True)
1661 d.addCallback(self.wait_for_operation, "124")
1662 def _check_json(data):
1663 self.failUnlessEqual(data["finished"], True)
1664 self.failUnlessEqual(data["count-objects-checked"], 8)
1665 self.failUnlessEqual(data["count-objects-healthy-pre-repair"], 8)
1666 self.failUnlessEqual(data["count-objects-unhealthy-pre-repair"], 0)
1667 self.failUnlessEqual(data["count-corrupt-shares-pre-repair"], 0)
1668 self.failUnlessEqual(data["count-repairs-attempted"], 0)
1669 self.failUnlessEqual(data["count-repairs-successful"], 0)
1670 self.failUnlessEqual(data["count-repairs-unsuccessful"], 0)
1671 self.failUnlessEqual(data["count-objects-healthy-post-repair"], 8)
1672 self.failUnlessEqual(data["count-objects-unhealthy-post-repair"], 0)
1673 self.failUnlessEqual(data["count-corrupt-shares-post-repair"], 0)
1674 d.addCallback(_check_json)
1675 d.addCallback(self.get_operation_results, "124", "html")
1676 def _check_html(res):
1677 self.failUnless("Objects Checked: <span>8</span>" in res)
1679 self.failUnless("Objects Healthy (before repair): <span>8</span>" in res)
1680 self.failUnless("Objects Unhealthy (before repair): <span>0</span>" in res)
1681 self.failUnless("Corrupt Shares (before repair): <span>0</span>" in res)
1683 self.failUnless("Repairs Attempted: <span>0</span>" in res)
1684 self.failUnless("Repairs Successful: <span>0</span>" in res)
1685 self.failUnless("Repairs Unsuccessful: <span>0</span>" in res)
1687 self.failUnless("Objects Healthy (after repair): <span>8</span>" in res)
1688 self.failUnless("Objects Unhealthy (after repair): <span>0</span>" in res)
1689 self.failUnless("Corrupt Shares (after repair): <span>0</span>" in res)
1690 d.addCallback(_check_html)
1693 def test_POST_FILEURL_bad_t(self):
1694 d = self.shouldFail2(error.Error, "POST_bad_t", "400 Bad Request",
1695 "POST to file: bad t=bogus",
1696 self.POST, self.public_url + "/foo/bar.txt",
1700 def test_POST_mkdir(self): # return value?
1701 d = self.POST(self.public_url + "/foo", t="mkdir", name="newdir")
1702 d.addCallback(lambda res: self._foo_node.get(u"newdir"))
1703 d.addCallback(self.failUnlessNodeKeysAre, [])
1706 def test_POST_mkdir_2(self):
1707 d = self.POST(self.public_url + "/foo/newdir?t=mkdir", "")
1708 d.addCallback(lambda res:
1709 self.failUnlessNodeHasChild(self._foo_node, u"newdir"))
1710 d.addCallback(lambda res: self._foo_node.get(u"newdir"))
1711 d.addCallback(self.failUnlessNodeKeysAre, [])
1714 def test_POST_mkdirs_2(self):
1715 d = self.POST(self.public_url + "/foo/bardir/newdir?t=mkdir", "")
1716 d.addCallback(lambda res:
1717 self.failUnlessNodeHasChild(self._foo_node, u"bardir"))
1718 d.addCallback(lambda res: self._foo_node.get(u"bardir"))
1719 d.addCallback(lambda bardirnode: bardirnode.get(u"newdir"))
1720 d.addCallback(self.failUnlessNodeKeysAre, [])
1723 def test_POST_mkdir_no_parentdir_noredirect(self):
1724 d = self.POST("/uri?t=mkdir")
1725 def _after_mkdir(res):
1726 uri.NewDirectoryURI.init_from_string(res)
1727 d.addCallback(_after_mkdir)
1730 def test_POST_mkdir_no_parentdir_redirect(self):
1731 d = self.POST("/uri?t=mkdir&redirect_to_result=true")
1732 d.addBoth(self.shouldRedirect, None, statuscode='303')
1733 def _check_target(target):
1734 target = urllib.unquote(target)
1735 self.failUnless(target.startswith("uri/URI:DIR2:"), target)
1736 d.addCallback(_check_target)
1739 def test_POST_noparent_bad(self):
1740 d = self.shouldHTTPError2("POST /uri?t=bogus", 400, "Bad Request",
1741 "/uri accepts only PUT, PUT?t=mkdir, "
1742 "POST?t=upload, and POST?t=mkdir",
1743 self.POST, "/uri?t=bogus")
1746 def test_welcome_page_mkdir_button(self):
1747 # Fetch the welcome page.
1749 def _after_get_welcome_page(res):
1750 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)
1751 mo = MKDIR_BUTTON_RE.search(res)
1752 formaction = mo.group(1)
1754 formaname = mo.group(3)
1755 formavalue = mo.group(4)
1756 return (formaction, formt, formaname, formavalue)
1757 d.addCallback(_after_get_welcome_page)
1758 def _after_parse_form(res):
1759 (formaction, formt, formaname, formavalue) = res
1760 return self.POST("/%s?t=%s&%s=%s" % (formaction, formt, formaname, formavalue))
1761 d.addCallback(_after_parse_form)
1762 d.addBoth(self.shouldRedirect, None, statuscode='303')
1765 def test_POST_mkdir_replace(self): # return value?
1766 d = self.POST(self.public_url + "/foo", t="mkdir", name="sub")
1767 d.addCallback(lambda res: self._foo_node.get(u"sub"))
1768 d.addCallback(self.failUnlessNodeKeysAre, [])
1771 def test_POST_mkdir_no_replace_queryarg(self): # return value?
1772 d = self.POST(self.public_url + "/foo?replace=false", t="mkdir", name="sub")
1773 d.addBoth(self.shouldFail, error.Error,
1774 "POST_mkdir_no_replace_queryarg",
1776 "There was already a child by that name, and you asked me "
1777 "to not replace it")
1778 d.addCallback(lambda res: self._foo_node.get(u"sub"))
1779 d.addCallback(self.failUnlessNodeKeysAre, [u"baz.txt"])
1782 def test_POST_mkdir_no_replace_field(self): # return value?
1783 d = self.POST(self.public_url + "/foo", t="mkdir", name="sub",
1785 d.addBoth(self.shouldFail, error.Error, "POST_mkdir_no_replace_field",
1787 "There was already a child by that name, and you asked me "
1788 "to not replace it")
1789 d.addCallback(lambda res: self._foo_node.get(u"sub"))
1790 d.addCallback(self.failUnlessNodeKeysAre, [u"baz.txt"])
1793 def test_POST_mkdir_whendone_field(self):
1794 d = self.POST(self.public_url + "/foo",
1795 t="mkdir", name="newdir", when_done="/THERE")
1796 d.addBoth(self.shouldRedirect, "/THERE")
1797 d.addCallback(lambda res: self._foo_node.get(u"newdir"))
1798 d.addCallback(self.failUnlessNodeKeysAre, [])
1801 def test_POST_mkdir_whendone_queryarg(self):
1802 d = self.POST(self.public_url + "/foo?when_done=/THERE",
1803 t="mkdir", name="newdir")
1804 d.addBoth(self.shouldRedirect, "/THERE")
1805 d.addCallback(lambda res: self._foo_node.get(u"newdir"))
1806 d.addCallback(self.failUnlessNodeKeysAre, [])
1809 def test_POST_bad_t(self):
1810 d = self.shouldFail2(error.Error, "POST_bad_t", "400 Bad Request",
1811 "POST to a directory with bad t=BOGUS",
1812 self.POST, self.public_url + "/foo", t="BOGUS")
1815 def test_POST_set_children(self):
1816 contents9, n9, newuri9 = self.makefile(9)
1817 contents10, n10, newuri10 = self.makefile(10)
1818 contents11, n11, newuri11 = self.makefile(11)
1821 "atomic_added_1": [ "filenode", { "rw_uri": "%s",
1824 "ctime": 1002777696.7564139,
1825 "mtime": 1002777696.7564139
1828 "atomic_added_2": [ "filenode", { "rw_uri": "%s",
1831 "ctime": 1002777696.7564139,
1832 "mtime": 1002777696.7564139
1835 "atomic_added_3": [ "filenode", { "rw_uri": "%s",
1838 "ctime": 1002777696.7564139,
1839 "mtime": 1002777696.7564139
1842 }""" % (newuri9, newuri10, newuri11)
1844 url = self.webish_url + self.public_url + "/foo" + "?t=set_children"
1846 d = client.getPage(url, method="POST", postdata=reqbody)
1848 self.failUnlessURIMatchesChild(newuri9, self._foo_node, u"atomic_added_1")
1849 self.failUnlessURIMatchesChild(newuri10, self._foo_node, u"atomic_added_2")
1850 self.failUnlessURIMatchesChild(newuri11, self._foo_node, u"atomic_added_3")
1852 d.addCallback(_then)
1853 d.addErrback(self.dump_error)
1856 def test_POST_put_uri(self):
1857 contents, n, newuri = self.makefile(8)
1858 d = self.POST(self.public_url + "/foo", t="uri", name="new.txt", uri=newuri)
1859 d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"new.txt")
1860 d.addCallback(lambda res:
1861 self.failUnlessChildContentsAre(self._foo_node, u"new.txt",
1865 def test_POST_put_uri_replace(self):
1866 contents, n, newuri = self.makefile(8)
1867 d = self.POST(self.public_url + "/foo", t="uri", name="bar.txt", uri=newuri)
1868 d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"bar.txt")
1869 d.addCallback(lambda res:
1870 self.failUnlessChildContentsAre(self._foo_node, u"bar.txt",
1874 def test_POST_put_uri_no_replace_queryarg(self):
1875 contents, n, newuri = self.makefile(8)
1876 d = self.POST(self.public_url + "/foo?replace=false", t="uri",
1877 name="bar.txt", uri=newuri)
1878 d.addBoth(self.shouldFail, error.Error,
1879 "POST_put_uri_no_replace_queryarg",
1881 "There was already a child by that name, and you asked me "
1882 "to not replace it")
1883 d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt"))
1884 d.addCallback(self.failUnlessIsBarDotTxt)
1887 def test_POST_put_uri_no_replace_field(self):
1888 contents, n, newuri = self.makefile(8)
1889 d = self.POST(self.public_url + "/foo", t="uri", replace="false",
1890 name="bar.txt", uri=newuri)
1891 d.addBoth(self.shouldFail, error.Error,
1892 "POST_put_uri_no_replace_field",
1894 "There was already a child by that name, and you asked me "
1895 "to not replace it")
1896 d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt"))
1897 d.addCallback(self.failUnlessIsBarDotTxt)
1900 def test_POST_delete(self):
1901 d = self.POST(self.public_url + "/foo", t="delete", name="bar.txt")
1902 d.addCallback(lambda res: self._foo_node.list())
1903 def _check(children):
1904 self.failIf(u"bar.txt" in children)
1905 d.addCallback(_check)
1908 def test_POST_rename_file(self):
1909 d = self.POST(self.public_url + "/foo", t="rename",
1910 from_name="bar.txt", to_name='wibble.txt')
1911 d.addCallback(lambda res:
1912 self.failIfNodeHasChild(self._foo_node, u"bar.txt"))
1913 d.addCallback(lambda res:
1914 self.failUnlessNodeHasChild(self._foo_node, u"wibble.txt"))
1915 d.addCallback(lambda res: self.GET(self.public_url + "/foo/wibble.txt"))
1916 d.addCallback(self.failUnlessIsBarDotTxt)
1917 d.addCallback(lambda res: self.GET(self.public_url + "/foo/wibble.txt?t=json"))
1918 d.addCallback(self.failUnlessIsBarJSON)
1921 def test_POST_rename_file_redundant(self):
1922 d = self.POST(self.public_url + "/foo", t="rename",
1923 from_name="bar.txt", to_name='bar.txt')
1924 d.addCallback(lambda res:
1925 self.failUnlessNodeHasChild(self._foo_node, u"bar.txt"))
1926 d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt"))
1927 d.addCallback(self.failUnlessIsBarDotTxt)
1928 d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt?t=json"))
1929 d.addCallback(self.failUnlessIsBarJSON)
1932 def test_POST_rename_file_replace(self):
1933 # rename a file and replace a directory with it
1934 d = self.POST(self.public_url + "/foo", t="rename",
1935 from_name="bar.txt", to_name='empty')
1936 d.addCallback(lambda res:
1937 self.failIfNodeHasChild(self._foo_node, u"bar.txt"))
1938 d.addCallback(lambda res:
1939 self.failUnlessNodeHasChild(self._foo_node, u"empty"))
1940 d.addCallback(lambda res: self.GET(self.public_url + "/foo/empty"))
1941 d.addCallback(self.failUnlessIsBarDotTxt)
1942 d.addCallback(lambda res: self.GET(self.public_url + "/foo/empty?t=json"))
1943 d.addCallback(self.failUnlessIsBarJSON)
1946 def test_POST_rename_file_no_replace_queryarg(self):
1947 # rename a file and replace a directory with it
1948 d = self.POST(self.public_url + "/foo?replace=false", t="rename",
1949 from_name="bar.txt", to_name='empty')
1950 d.addBoth(self.shouldFail, error.Error,
1951 "POST_rename_file_no_replace_queryarg",
1953 "There was already a child by that name, and you asked me "
1954 "to not replace it")
1955 d.addCallback(lambda res: self.GET(self.public_url + "/foo/empty?t=json"))
1956 d.addCallback(self.failUnlessIsEmptyJSON)
1959 def test_POST_rename_file_no_replace_field(self):
1960 # rename a file and replace a directory with it
1961 d = self.POST(self.public_url + "/foo", t="rename", replace="false",
1962 from_name="bar.txt", to_name='empty')
1963 d.addBoth(self.shouldFail, error.Error,
1964 "POST_rename_file_no_replace_field",
1966 "There was already a child by that name, and you asked me "
1967 "to not replace it")
1968 d.addCallback(lambda res: self.GET(self.public_url + "/foo/empty?t=json"))
1969 d.addCallback(self.failUnlessIsEmptyJSON)
1972 def failUnlessIsEmptyJSON(self, res):
1973 data = simplejson.loads(res)
1974 self.failUnlessEqual(data[0], "dirnode", data)
1975 self.failUnlessEqual(len(data[1]["children"]), 0)
1977 def test_POST_rename_file_slash_fail(self):
1978 d = self.POST(self.public_url + "/foo", t="rename",
1979 from_name="bar.txt", to_name='kirk/spock.txt')
1980 d.addBoth(self.shouldFail, error.Error,
1981 "test_POST_rename_file_slash_fail",
1983 "to_name= may not contain a slash",
1985 d.addCallback(lambda res:
1986 self.failUnlessNodeHasChild(self._foo_node, u"bar.txt"))
1989 def test_POST_rename_dir(self):
1990 d = self.POST(self.public_url, t="rename",
1991 from_name="foo", to_name='plunk')
1992 d.addCallback(lambda res:
1993 self.failIfNodeHasChild(self.public_root, u"foo"))
1994 d.addCallback(lambda res:
1995 self.failUnlessNodeHasChild(self.public_root, u"plunk"))
1996 d.addCallback(lambda res: self.GET(self.public_url + "/plunk?t=json"))
1997 d.addCallback(self.failUnlessIsFooJSON)
2000 def shouldRedirect(self, res, target=None, statuscode=None, which=""):
2001 """ If target is not None then the redirection has to go to target. If
2002 statuscode is not None then the redirection has to be accomplished with
2003 that HTTP status code."""
2004 if not isinstance(res, failure.Failure):
2005 to_where = (target is None) and "somewhere" or ("to " + target)
2006 self.fail("%s: we were expecting to get redirected %s, not get an"
2007 " actual page: %s" % (which, to_where, res))
2008 res.trap(error.PageRedirect)
2009 if statuscode is not None:
2010 self.failUnlessEqual(res.value.status, statuscode,
2011 "%s: not a redirect" % which)
2012 if target is not None:
2013 # the PageRedirect does not seem to capture the uri= query arg
2014 # properly, so we can't check for it.
2015 realtarget = self.webish_url + target
2016 self.failUnlessEqual(res.value.location, realtarget,
2017 "%s: wrong target" % which)
2018 return res.value.location
2020 def test_GET_URI_form(self):
2021 base = "/uri?uri=%s" % self._bar_txt_uri
2022 # this is supposed to give us a redirect to /uri/$URI, plus arguments
2023 targetbase = "/uri/%s" % urllib.quote(self._bar_txt_uri)
2025 d.addBoth(self.shouldRedirect, targetbase)
2026 d.addCallback(lambda res: self.GET(base+"&filename=bar.txt"))
2027 d.addBoth(self.shouldRedirect, targetbase+"?filename=bar.txt")
2028 d.addCallback(lambda res: self.GET(base+"&t=json"))
2029 d.addBoth(self.shouldRedirect, targetbase+"?t=json")
2030 d.addCallback(self.log, "about to get file by uri")
2031 d.addCallback(lambda res: self.GET(base, followRedirect=True))
2032 d.addCallback(self.failUnlessIsBarDotTxt)
2033 d.addCallback(self.log, "got file by uri, about to get dir by uri")
2034 d.addCallback(lambda res: self.GET("/uri?uri=%s&t=json" % self._foo_uri,
2035 followRedirect=True))
2036 d.addCallback(self.failUnlessIsFooJSON)
2037 d.addCallback(self.log, "got dir by uri")
2041 def test_GET_URI_form_bad(self):
2042 d = self.shouldFail2(error.Error, "test_GET_URI_form_bad",
2043 "400 Bad Request", "GET /uri requires uri=",
2047 def test_GET_rename_form(self):
2048 d = self.GET(self.public_url + "/foo?t=rename-form&name=bar.txt",
2049 followRedirect=True)
2051 self.failUnless('name="when_done" value="."' in res, res)
2052 self.failUnless(re.search(r'name="from_name" value="bar\.txt"', res))
2053 d.addCallback(_check)
2056 def log(self, res, msg):
2057 #print "MSG: %s RES: %s" % (msg, res)
2061 def test_GET_URI_URL(self):
2062 base = "/uri/%s" % self._bar_txt_uri
2064 d.addCallback(self.failUnlessIsBarDotTxt)
2065 d.addCallback(lambda res: self.GET(base+"?filename=bar.txt"))
2066 d.addCallback(self.failUnlessIsBarDotTxt)
2067 d.addCallback(lambda res: self.GET(base+"?filename=bar.txt&save=true"))
2068 d.addCallback(self.failUnlessIsBarDotTxt)
2071 def test_GET_URI_URL_dir(self):
2072 base = "/uri/%s?t=json" % self._foo_uri
2074 d.addCallback(self.failUnlessIsFooJSON)
2077 def test_GET_URI_URL_missing(self):
2078 base = "/uri/%s" % self._bad_file_uri
2080 d.addBoth(self.shouldHTTPError, "test_GET_URI_URL_missing",
2081 http.GONE, response_substring="NotEnoughSharesError")
2082 # TODO: how can we exercise both sides of WebDownloadTarget.fail
2083 # here? we must arrange for a download to fail after target.open()
2084 # has been called, and then inspect the response to see that it is
2085 # shorter than we expected.
2088 def test_PUT_NEWFILEURL_uri(self):
2089 contents, n, new_uri = self.makefile(8)
2090 d = self.PUT(self.public_url + "/foo/new.txt?t=uri", new_uri)
2091 d.addCallback(lambda res: self.failUnlessEqual(res.strip(), new_uri))
2092 d.addCallback(lambda res:
2093 self.failUnlessChildContentsAre(self._foo_node, u"new.txt",
2097 def test_PUT_NEWFILEURL_uri_replace(self):
2098 contents, n, new_uri = self.makefile(8)
2099 d = self.PUT(self.public_url + "/foo/bar.txt?t=uri", new_uri)
2100 d.addCallback(lambda res: self.failUnlessEqual(res.strip(), new_uri))
2101 d.addCallback(lambda res:
2102 self.failUnlessChildContentsAre(self._foo_node, u"bar.txt",
2106 def test_PUT_NEWFILEURL_uri_no_replace(self):
2107 contents, n, new_uri = self.makefile(8)
2108 d = self.PUT(self.public_url + "/foo/bar.txt?t=uri&replace=false", new_uri)
2109 d.addBoth(self.shouldFail, error.Error, "PUT_NEWFILEURL_uri_no_replace",
2111 "There was already a child by that name, and you asked me "
2112 "to not replace it")
2115 def test_PUT_NEWFILE_URI(self):
2116 file_contents = "New file contents here\n"
2117 d = self.PUT("/uri", file_contents)
2119 self.failUnless(uri in FakeCHKFileNode.all_contents)
2120 self.failUnlessEqual(FakeCHKFileNode.all_contents[uri],
2122 return self.GET("/uri/%s" % uri)
2123 d.addCallback(_check)
2125 self.failUnlessEqual(res, file_contents)
2126 d.addCallback(_check2)
2129 def test_PUT_NEWFILE_URI_only_PUT(self):
2130 d = self.PUT("/uri?t=bogus", "")
2131 d.addBoth(self.shouldFail, error.Error,
2132 "PUT_NEWFILE_URI_only_PUT",
2134 "/uri accepts only PUT, PUT?t=mkdir, POST?t=upload, and POST?t=mkdir")
2137 def test_PUT_NEWFILE_URI_mutable(self):
2138 file_contents = "New file contents here\n"
2139 d = self.PUT("/uri?mutable=true", file_contents)
2140 def _check_mutable(uri):
2143 self.failUnless(IMutableFileURI.providedBy(u))
2144 self.failUnless(u.storage_index in FakeMutableFileNode.all_contents)
2145 n = self.s.create_node_from_uri(uri)
2146 return n.download_best_version()
2147 d.addCallback(_check_mutable)
2148 def _check2_mutable(data):
2149 self.failUnlessEqual(data, file_contents)
2150 d.addCallback(_check2_mutable)
2154 self.failUnless(uri in FakeCHKFileNode.all_contents)
2155 self.failUnlessEqual(FakeCHKFileNode.all_contents[uri],
2157 return self.GET("/uri/%s" % uri)
2158 d.addCallback(_check)
2160 self.failUnlessEqual(res, file_contents)
2161 d.addCallback(_check2)
2164 def test_PUT_mkdir(self):
2165 d = self.PUT("/uri?t=mkdir", "")
2167 n = self.s.create_node_from_uri(uri.strip())
2168 d2 = self.failUnlessNodeKeysAre(n, [])
2169 d2.addCallback(lambda res:
2170 self.GET("/uri/%s?t=json" % uri))
2172 d.addCallback(_check)
2173 d.addCallback(self.failUnlessIsEmptyJSON)
2176 def test_POST_check(self):
2177 d = self.POST(self.public_url + "/foo", t="check", name="bar.txt")
2179 # this returns a string form of the results, which are probably
2180 # None since we're using fake filenodes.
2181 # TODO: verify that the check actually happened, by changing
2182 # FakeCHKFileNode to count how many times .check() has been
2185 d.addCallback(_done)
2188 def test_bad_method(self):
2189 url = self.webish_url + self.public_url + "/foo/bar.txt"
2190 d = self.shouldHTTPError2("test_bad_method",
2191 501, "Not Implemented",
2192 "I don't know how to treat a BOGUS request.",
2193 client.getPage, url, method="BOGUS")
2196 def test_short_url(self):
2197 url = self.webish_url + "/uri"
2198 d = self.shouldHTTPError2("test_short_url", 501, "Not Implemented",
2199 "I don't know how to treat a DELETE request.",
2200 client.getPage, url, method="DELETE")
2203 def test_ophandle_bad(self):
2204 url = self.webish_url + "/operations/bogus?t=status"
2205 d = self.shouldHTTPError2("test_ophandle_bad", 404, "404 Not Found",
2206 "unknown/expired handle 'bogus'",
2207 client.getPage, url)
2210 def test_ophandle_cancel(self):
2211 d = self.POST(self.public_url + "/foo/?t=start-manifest&ophandle=128",
2212 followRedirect=True)
2213 d.addCallback(lambda ignored:
2214 self.GET("/operations/128?t=status&output=JSON"))
2216 data = simplejson.loads(res)
2217 self.failUnless("finished" in data, res)
2218 monitor = self.ws.root.child_operations.handles["128"][0]
2219 d = self.POST("/operations/128?t=cancel&output=JSON")
2221 data = simplejson.loads(res)
2222 self.failUnless("finished" in data, res)
2223 # t=cancel causes the handle to be forgotten
2224 self.failUnless(monitor.is_cancelled())
2225 d.addCallback(_check2)
2227 d.addCallback(_check1)
2228 d.addCallback(lambda ignored:
2229 self.shouldHTTPError2("test_ophandle_cancel",
2230 404, "404 Not Found",
2231 "unknown/expired handle '128'",
2233 "/operations/128?t=status&output=JSON"))
2236 def test_ophandle_retainfor(self):
2237 d = self.POST(self.public_url + "/foo/?t=start-manifest&ophandle=129&retain-for=60",
2238 followRedirect=True)
2239 d.addCallback(lambda ignored:
2240 self.GET("/operations/129?t=status&output=JSON&retain-for=0"))
2242 data = simplejson.loads(res)
2243 self.failUnless("finished" in data, res)
2244 d.addCallback(_check1)
2245 # the retain-for=0 will cause the handle to be expired very soon
2246 d.addCallback(self.stall, 2.0)
2247 d.addCallback(lambda ignored:
2248 self.shouldHTTPError2("test_ophandle_retainfor",
2249 404, "404 Not Found",
2250 "unknown/expired handle '129'",
2252 "/operations/129?t=status&output=JSON"))
2255 def test_ophandle_release_after_complete(self):
2256 d = self.POST(self.public_url + "/foo/?t=start-manifest&ophandle=130",
2257 followRedirect=True)
2258 d.addCallback(self.wait_for_operation, "130")
2259 d.addCallback(lambda ignored:
2260 self.GET("/operations/130?t=status&output=JSON&release-after-complete=true"))
2261 # the release-after-complete=true will cause the handle to be expired
2262 d.addCallback(lambda ignored:
2263 self.shouldHTTPError2("test_ophandle_release_after_complete",
2264 404, "404 Not Found",
2265 "unknown/expired handle '130'",
2267 "/operations/130?t=status&output=JSON"))
2270 def test_incident(self):
2271 d = self.POST("/report_incident", details="eek")
2273 self.failUnless("Thank you for your report!" in res, res)
2274 d.addCallback(_done)
2278 class Util(unittest.TestCase):
2279 def test_abbreviate_time(self):
2280 self.failUnlessEqual(common.abbreviate_time(None), "")
2281 self.failUnlessEqual(common.abbreviate_time(1.234), "1.23s")
2282 self.failUnlessEqual(common.abbreviate_time(0.123), "123ms")
2283 self.failUnlessEqual(common.abbreviate_time(0.00123), "1.2ms")
2284 self.failUnlessEqual(common.abbreviate_time(0.000123), "123us")
2286 def test_abbreviate_rate(self):
2287 self.failUnlessEqual(common.abbreviate_rate(None), "")
2288 self.failUnlessEqual(common.abbreviate_rate(1234000), "1.23MBps")
2289 self.failUnlessEqual(common.abbreviate_rate(12340), "12.3kBps")
2290 self.failUnlessEqual(common.abbreviate_rate(123), "123Bps")
2292 def test_abbreviate_size(self):
2293 self.failUnlessEqual(common.abbreviate_size(None), "")
2294 self.failUnlessEqual(common.abbreviate_size(1.23*1000*1000*1000), "1.23GB")
2295 self.failUnlessEqual(common.abbreviate_size(1.23*1000*1000), "1.23MB")
2296 self.failUnlessEqual(common.abbreviate_size(1230), "1.2kB")
2297 self.failUnlessEqual(common.abbreviate_size(123), "123B")