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)
103 class WebMixin(object):
105 self.s = FakeClient()
106 self.s.startService()
107 self.ws = s = webish.WebishServer("0")
108 s.setServiceParent(self.s)
109 self.webish_port = port = s.listener._port.getHost().port
110 self.webish_url = "http://localhost:%d" % port
112 l = [ self.s.create_empty_dirnode() for x in range(6) ]
113 d = defer.DeferredList(l)
115 self.public_root = res[0][1]
116 assert interfaces.IDirectoryNode.providedBy(self.public_root), res
117 self.public_url = "/uri/" + self.public_root.get_uri()
118 self.private_root = res[1][1]
122 self._foo_uri = foo.get_uri()
123 self._foo_readonly_uri = foo.get_readonly_uri()
124 # NOTE: we ignore the deferred on all set_uri() calls, because we
125 # know the fake nodes do these synchronously
126 self.public_root.set_uri(u"foo", foo.get_uri())
128 self.BAR_CONTENTS, n, self._bar_txt_uri = self.makefile(0)
129 foo.set_uri(u"bar.txt", self._bar_txt_uri)
131 foo.set_uri(u"empty", res[3][1].get_uri())
132 sub_uri = res[4][1].get_uri()
133 self._sub_uri = sub_uri
134 foo.set_uri(u"sub", sub_uri)
135 sub = self.s.create_node_from_uri(sub_uri)
137 _ign, n, blocking_uri = self.makefile(1)
138 foo.set_uri(u"blockingfile", blocking_uri)
140 unicode_filename = u"n\u00fc.txt" # n u-umlaut . t x t
141 # ok, unicode calls it LATIN SMALL LETTER U WITH DIAERESIS but I
142 # still think of it as an umlaut
143 foo.set_uri(unicode_filename, self._bar_txt_uri)
145 _ign, n, baz_file = self.makefile(2)
146 sub.set_uri(u"baz.txt", baz_file)
148 _ign, n, self._bad_file_uri = self.makefile(3)
149 # this uri should not be downloadable
150 del FakeCHKFileNode.all_contents[self._bad_file_uri]
153 self.public_root.set_uri(u"reedownlee", rodir.get_readonly_uri())
154 rodir.set_uri(u"nor", baz_file)
159 # public/foo/blockingfile
162 # public/foo/sub/baz.txt
164 # public/reedownlee/nor
165 self.NEWFILE_CONTENTS = "newfile contents\n"
167 return foo.get_metadata_for(u"bar.txt")
169 def _got_metadata(metadata):
170 self._bar_txt_metadata = metadata
171 d.addCallback(_got_metadata)
174 def makefile(self, number):
175 contents = "contents of file %s\n" % number
176 n = create_chk_filenode(self.s, contents)
177 return contents, n, n.get_uri()
180 return self.s.stopService()
182 def failUnlessIsBarDotTxt(self, res):
183 self.failUnlessEqual(res, self.BAR_CONTENTS, res)
185 def failUnlessIsBarJSON(self, res):
186 data = simplejson.loads(res)
187 self.failUnless(isinstance(data, list))
188 self.failUnlessEqual(data[0], "filenode")
189 self.failUnless(isinstance(data[1], dict))
190 self.failIf(data[1]["mutable"])
191 self.failIf("rw_uri" in data[1]) # immutable
192 self.failUnlessEqual(data[1]["ro_uri"], self._bar_txt_uri)
193 self.failUnlessEqual(data[1]["size"], len(self.BAR_CONTENTS))
195 def failUnlessIsFooJSON(self, res):
196 data = simplejson.loads(res)
197 self.failUnless(isinstance(data, list))
198 self.failUnlessEqual(data[0], "dirnode", res)
199 self.failUnless(isinstance(data[1], dict))
200 self.failUnless(data[1]["mutable"])
201 self.failUnless("rw_uri" in data[1]) # mutable
202 self.failUnlessEqual(data[1]["rw_uri"], self._foo_uri)
203 self.failUnlessEqual(data[1]["ro_uri"], self._foo_readonly_uri)
205 kidnames = sorted(data[1]["children"])
206 self.failUnlessEqual(kidnames,
207 [u"bar.txt", u"blockingfile", u"empty",
208 u"n\u00fc.txt", u"sub"])
209 kids = data[1]["children"]
210 self.failUnlessEqual(kids[u"sub"][0], "dirnode")
211 self.failUnless("metadata" in kids[u"sub"][1])
212 self.failUnless("ctime" in kids[u"sub"][1]["metadata"])
213 self.failUnless("mtime" in kids[u"sub"][1]["metadata"])
214 self.failUnlessEqual(kids[u"bar.txt"][0], "filenode")
215 self.failUnlessEqual(kids[u"bar.txt"][1]["size"], len(self.BAR_CONTENTS))
216 self.failUnlessEqual(kids[u"bar.txt"][1]["ro_uri"], self._bar_txt_uri)
217 self.failUnlessEqual(kids[u"bar.txt"][1]["metadata"]["ctime"],
218 self._bar_txt_metadata["ctime"])
219 self.failUnlessEqual(kids[u"n\u00fc.txt"][1]["ro_uri"],
222 def GET(self, urlpath, followRedirect=False):
223 assert not isinstance(urlpath, unicode)
224 url = self.webish_url + urlpath
225 return client.getPage(url, method="GET", followRedirect=followRedirect)
227 def HEAD(self, urlpath):
228 # this requires some surgery, because twisted.web.client doesn't want
229 # to give us back the response headers.
230 factory = HTTPClientHEADFactory(urlpath)
231 reactor.connectTCP("localhost", self.webish_port, factory)
232 return factory.deferred
234 def PUT(self, urlpath, data):
235 url = self.webish_url + urlpath
236 return client.getPage(url, method="PUT", postdata=data)
238 def DELETE(self, urlpath):
239 url = self.webish_url + urlpath
240 return client.getPage(url, method="DELETE")
242 def POST(self, urlpath, followRedirect=False, **fields):
243 url = self.webish_url + urlpath
244 sepbase = "boogabooga"
248 form.append('Content-Disposition: form-data; name="_charset"')
252 for name, value in fields.iteritems():
253 if isinstance(value, tuple):
254 filename, value = value
255 form.append('Content-Disposition: form-data; name="%s"; '
256 'filename="%s"' % (name, filename.encode("utf-8")))
258 form.append('Content-Disposition: form-data; name="%s"' % name)
260 if isinstance(value, unicode):
261 value = value.encode("utf-8")
264 assert isinstance(value, str)
268 body = "\r\n".join(form) + "\r\n"
269 headers = {"content-type": "multipart/form-data; boundary=%s" % sepbase,
271 return client.getPage(url, method="POST", postdata=body,
272 headers=headers, followRedirect=followRedirect)
274 def shouldFail(self, res, expected_failure, which,
275 substring=None, response_substring=None):
276 if isinstance(res, failure.Failure):
277 res.trap(expected_failure)
279 self.failUnless(substring in str(res),
280 "substring '%s' not in '%s'"
281 % (substring, str(res)))
282 if response_substring:
283 self.failUnless(response_substring in res.value.response,
284 "response substring '%s' not in '%s'"
285 % (response_substring, res.value.response))
287 self.fail("%s was supposed to raise %s, not get '%s'" %
288 (which, expected_failure, res))
290 def shouldFail2(self, expected_failure, which, substring,
292 callable, *args, **kwargs):
293 assert substring is None or isinstance(substring, str)
294 assert response_substring is None or isinstance(response_substring, str)
295 d = defer.maybeDeferred(callable, *args, **kwargs)
297 if isinstance(res, failure.Failure):
298 res.trap(expected_failure)
300 self.failUnless(substring in str(res),
301 "%s: substring '%s' not in '%s'"
302 % (which, substring, str(res)))
303 if response_substring:
304 self.failUnless(response_substring in res.value.response,
305 "%s: response substring '%s' not in '%s'"
307 response_substring, res.value.response))
309 self.fail("%s was supposed to raise %s, not get '%s'" %
310 (which, expected_failure, res))
314 def should404(self, res, which):
315 if isinstance(res, failure.Failure):
316 res.trap(error.Error)
317 self.failUnlessEqual(res.value.status, "404")
319 self.fail("%s was supposed to Error(404), not get '%s'" %
322 def shouldHTTPError(self, res, which, code=None, substring=None,
323 response_substring=None):
324 if isinstance(res, failure.Failure):
325 res.trap(error.Error)
327 self.failUnlessEqual(res.value.status, str(code))
329 self.failUnless(substring in str(res),
330 "substring '%s' not in '%s'"
331 % (substring, str(res)))
332 if response_substring:
333 self.failUnless(response_substring in res.value.response,
334 "response substring '%s' not in '%s'"
335 % (response_substring, res.value.response))
337 self.fail("%s was supposed to Error(%s), not get '%s'" %
340 def shouldHTTPError2(self, which,
341 code=None, substring=None, response_substring=None,
342 callable=None, *args, **kwargs):
343 assert substring is None or isinstance(substring, str)
345 d = defer.maybeDeferred(callable, *args, **kwargs)
346 d.addBoth(self.shouldHTTPError, which,
347 code, substring, response_substring)
351 class Web(WebMixin, unittest.TestCase):
352 def test_create(self):
355 def test_welcome(self):
358 self.failUnless('Welcome To AllMyData' in res)
359 self.failUnless('Tahoe' in res)
361 self.s.basedir = 'web/test_welcome'
362 fileutil.make_dirs("web/test_welcome")
363 fileutil.make_dirs("web/test_welcome/private")
365 d.addCallback(_check)
368 def test_provisioning_math(self):
369 self.failUnlessEqual(provisioning.binomial(10, 0), 1)
370 self.failUnlessEqual(provisioning.binomial(10, 1), 10)
371 self.failUnlessEqual(provisioning.binomial(10, 2), 45)
372 self.failUnlessEqual(provisioning.binomial(10, 9), 10)
373 self.failUnlessEqual(provisioning.binomial(10, 10), 1)
375 def test_provisioning(self):
376 d = self.GET("/provisioning/")
378 self.failUnless('Tahoe Provisioning Tool' in res)
379 fields = {'filled': True,
380 "num_users": int(50e3),
381 "files_per_user": 1000,
382 "space_per_user": int(1e9),
383 "sharing_ratio": 1.0,
384 "encoding_parameters": "3-of-10-5",
386 "ownership_mode": "A",
387 "download_rate": 100,
392 return self.POST("/provisioning/", **fields)
394 d.addCallback(_check)
396 self.failUnless('Tahoe Provisioning Tool' in res)
397 self.failUnless("Share space consumed: 167.01TB" in res)
399 fields = {'filled': True,
400 "num_users": int(50e6),
401 "files_per_user": 1000,
402 "space_per_user": int(5e9),
403 "sharing_ratio": 1.0,
404 "encoding_parameters": "25-of-100-50",
405 "num_servers": 30000,
406 "ownership_mode": "E",
407 "drive_failure_model": "U",
409 "download_rate": 1000,
414 return self.POST("/provisioning/", **fields)
415 d.addCallback(_check2)
417 self.failUnless("Share space consumed: huge!" in res)
418 fields = {'filled': True}
419 return self.POST("/provisioning/", **fields)
420 d.addCallback(_check3)
422 self.failUnless("Share space consumed:" in res)
423 d.addCallback(_check4)
426 def test_status(self):
427 dl_num = self.s.list_all_download_statuses()[0].get_counter()
428 ul_num = self.s.list_all_upload_statuses()[0].get_counter()
429 mu_num = self.s.list_all_mapupdate_statuses()[0].get_counter()
430 pub_num = self.s.list_all_publish_statuses()[0].get_counter()
431 ret_num = self.s.list_all_retrieve_statuses()[0].get_counter()
432 d = self.GET("/status", followRedirect=True)
434 self.failUnless('Upload and Download Status' in res, res)
435 self.failUnless('"down-%d"' % dl_num in res, res)
436 self.failUnless('"up-%d"' % ul_num in res, res)
437 self.failUnless('"mapupdate-%d"' % mu_num in res, res)
438 self.failUnless('"publish-%d"' % pub_num in res, res)
439 self.failUnless('"retrieve-%d"' % ret_num in res, res)
440 d.addCallback(_check)
441 d.addCallback(lambda res: self.GET("/status/?t=json"))
442 def _check_json(res):
443 data = simplejson.loads(res)
444 self.failUnless(isinstance(data, dict))
445 active = data["active"]
446 # TODO: test more. We need a way to fake an active operation
448 d.addCallback(_check_json)
450 d.addCallback(lambda res: self.GET("/status/down-%d" % dl_num))
452 self.failUnless("File Download Status" in res, res)
453 d.addCallback(_check_dl)
454 d.addCallback(lambda res: self.GET("/status/up-%d" % ul_num))
456 self.failUnless("File Upload Status" in res, res)
457 d.addCallback(_check_ul)
458 d.addCallback(lambda res: self.GET("/status/mapupdate-%d" % mu_num))
459 def _check_mapupdate(res):
460 self.failUnless("Mutable File Servermap Update Status" in res, res)
461 d.addCallback(_check_mapupdate)
462 d.addCallback(lambda res: self.GET("/status/publish-%d" % pub_num))
463 def _check_publish(res):
464 self.failUnless("Mutable File Publish Status" in res, res)
465 d.addCallback(_check_publish)
466 d.addCallback(lambda res: self.GET("/status/retrieve-%d" % ret_num))
467 def _check_retrieve(res):
468 self.failUnless("Mutable File Retrieve Status" in res, res)
469 d.addCallback(_check_retrieve)
473 def test_status_numbers(self):
474 drrm = status.DownloadResultsRendererMixin()
475 self.failUnlessEqual(drrm.render_time(None, None), "")
476 self.failUnlessEqual(drrm.render_time(None, 2.5), "2.50s")
477 self.failUnlessEqual(drrm.render_time(None, 0.25), "250ms")
478 self.failUnlessEqual(drrm.render_time(None, 0.0021), "2.1ms")
479 self.failUnlessEqual(drrm.render_time(None, 0.000123), "123us")
480 self.failUnlessEqual(drrm.render_rate(None, None), "")
481 self.failUnlessEqual(drrm.render_rate(None, 2500000), "2.50MBps")
482 self.failUnlessEqual(drrm.render_rate(None, 30100), "30.1kBps")
483 self.failUnlessEqual(drrm.render_rate(None, 123), "123Bps")
485 urrm = status.UploadResultsRendererMixin()
486 self.failUnlessEqual(urrm.render_time(None, None), "")
487 self.failUnlessEqual(urrm.render_time(None, 2.5), "2.50s")
488 self.failUnlessEqual(urrm.render_time(None, 0.25), "250ms")
489 self.failUnlessEqual(urrm.render_time(None, 0.0021), "2.1ms")
490 self.failUnlessEqual(urrm.render_time(None, 0.000123), "123us")
491 self.failUnlessEqual(urrm.render_rate(None, None), "")
492 self.failUnlessEqual(urrm.render_rate(None, 2500000), "2.50MBps")
493 self.failUnlessEqual(urrm.render_rate(None, 30100), "30.1kBps")
494 self.failUnlessEqual(urrm.render_rate(None, 123), "123Bps")
496 def test_GET_FILEURL(self):
497 d = self.GET(self.public_url + "/foo/bar.txt")
498 d.addCallback(self.failUnlessIsBarDotTxt)
501 def test_HEAD_FILEURL(self):
502 d = self.HEAD(self.public_url + "/foo/bar.txt")
504 self.failUnlessEqual(headers["content-length"][0],
505 str(len(self.BAR_CONTENTS)))
506 self.failUnlessEqual(headers["content-type"], ["text/plain"])
510 def test_GET_FILEURL_named(self):
511 base = "/file/%s" % urllib.quote(self._bar_txt_uri)
512 base2 = "/named/%s" % urllib.quote(self._bar_txt_uri)
513 d = self.GET(base + "/@@name=/blah.txt")
514 d.addCallback(self.failUnlessIsBarDotTxt)
515 d.addCallback(lambda res: self.GET(base + "/blah.txt"))
516 d.addCallback(self.failUnlessIsBarDotTxt)
517 d.addCallback(lambda res: self.GET(base + "/ignore/lots/blah.txt"))
518 d.addCallback(self.failUnlessIsBarDotTxt)
519 d.addCallback(lambda res: self.GET(base2 + "/@@name=/blah.txt"))
520 d.addCallback(self.failUnlessIsBarDotTxt)
521 save_url = base + "?save=true&filename=blah.txt"
522 d.addCallback(lambda res: self.GET(save_url))
523 d.addCallback(self.failUnlessIsBarDotTxt) # TODO: check headers
524 u_filename = u"n\u00e9wer.txt" # n e-acute w e r . t x t
525 u_fn_e = urllib.quote(u_filename.encode("utf-8"))
526 u_url = base + "?save=true&filename=" + u_fn_e
527 d.addCallback(lambda res: self.GET(u_url))
528 d.addCallback(self.failUnlessIsBarDotTxt) # TODO: check headers
531 def test_PUT_FILEURL_named_bad(self):
532 base = "/file/%s" % urllib.quote(self._bar_txt_uri)
533 d = self.shouldFail2(error.Error, "test_PUT_FILEURL_named_bad",
535 "/file can only be used with GET or HEAD",
536 self.PUT, base + "/@@name=/blah.txt", "")
539 def test_GET_DIRURL_named_bad(self):
540 base = "/file/%s" % urllib.quote(self._foo_uri)
541 d = self.shouldFail2(error.Error, "test_PUT_DIRURL_named_bad",
544 self.GET, base + "/@@name=/blah.txt")
547 def test_GET_slash_file_bad(self):
548 d = self.shouldFail2(error.Error, "test_GET_slash_file_bad",
550 "/file must be followed by a file-cap and a name",
554 def test_GET_unhandled_URI_named(self):
555 contents, n, newuri = self.makefile(12)
556 verifier_cap = n.get_verifier().to_string()
557 base = "/file/%s" % urllib.quote(verifier_cap)
558 # client.create_node_from_uri() can't handle verify-caps
559 d = self.shouldFail2(error.Error, "GET_unhandled_URI_named",
561 "is not a valid file- or directory- cap",
565 def test_GET_unhandled_URI(self):
566 contents, n, newuri = self.makefile(12)
567 verifier_cap = n.get_verifier().to_string()
568 base = "/uri/%s" % urllib.quote(verifier_cap)
569 # client.create_node_from_uri() can't handle verify-caps
570 d = self.shouldFail2(error.Error, "test_GET_unhandled_URI",
572 "is not a valid file- or directory- cap",
576 def test_GET_FILE_URI(self):
577 base = "/uri/%s" % urllib.quote(self._bar_txt_uri)
579 d.addCallback(self.failUnlessIsBarDotTxt)
582 def test_GET_FILE_URI_badchild(self):
583 base = "/uri/%s/boguschild" % urllib.quote(self._bar_txt_uri)
584 errmsg = "Files have no children, certainly not named 'boguschild'"
585 d = self.shouldFail2(error.Error, "test_GET_FILE_URI_badchild",
586 "400 Bad Request", errmsg,
590 def test_PUT_FILE_URI_badchild(self):
591 base = "/uri/%s/boguschild" % urllib.quote(self._bar_txt_uri)
592 errmsg = "Cannot create directory 'boguschild', because its parent is a file, not a directory"
593 d = self.shouldFail2(error.Error, "test_GET_FILE_URI_badchild",
594 "400 Bad Request", errmsg,
598 def test_GET_FILEURL_save(self):
599 d = self.GET(self.public_url + "/foo/bar.txt?filename=bar.txt&save=true")
600 # TODO: look at the headers, expect a Content-Disposition: attachment
602 d.addCallback(self.failUnlessIsBarDotTxt)
605 def test_GET_FILEURL_missing(self):
606 d = self.GET(self.public_url + "/foo/missing")
607 d.addBoth(self.should404, "test_GET_FILEURL_missing")
610 def test_PUT_NEWFILEURL(self):
611 d = self.PUT(self.public_url + "/foo/new.txt", self.NEWFILE_CONTENTS)
612 # TODO: we lose the response code, so we can't check this
613 #self.failUnlessEqual(responsecode, 201)
614 d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"new.txt")
615 d.addCallback(lambda res:
616 self.failUnlessChildContentsAre(self._foo_node, u"new.txt",
617 self.NEWFILE_CONTENTS))
620 def test_PUT_NEWFILEURL_mutable(self):
621 d = self.PUT(self.public_url + "/foo/new.txt?mutable=true",
622 self.NEWFILE_CONTENTS)
623 # TODO: we lose the response code, so we can't check this
624 #self.failUnlessEqual(responsecode, 201)
626 u = uri.from_string_mutable_filenode(res)
627 self.failUnless(u.is_mutable())
628 self.failIf(u.is_readonly())
630 d.addCallback(_check_uri)
631 d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"new.txt")
632 d.addCallback(lambda res:
633 self.failUnlessMutableChildContentsAre(self._foo_node,
635 self.NEWFILE_CONTENTS))
638 def test_PUT_NEWFILEURL_mutable_toobig(self):
639 d = self.shouldFail2(error.Error, "test_PUT_NEWFILEURL_mutable_toobig",
640 "413 Request Entity Too Large",
641 "SDMF is limited to one segment, and 10001 > 10000",
643 self.public_url + "/foo/new.txt?mutable=true",
644 "b" * (self.s.MUTABLE_SIZELIMIT+1))
647 def test_PUT_NEWFILEURL_replace(self):
648 d = self.PUT(self.public_url + "/foo/bar.txt", self.NEWFILE_CONTENTS)
649 # TODO: we lose the response code, so we can't check this
650 #self.failUnlessEqual(responsecode, 200)
651 d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"bar.txt")
652 d.addCallback(lambda res:
653 self.failUnlessChildContentsAre(self._foo_node, u"bar.txt",
654 self.NEWFILE_CONTENTS))
657 def test_PUT_NEWFILEURL_bad_t(self):
658 d = self.shouldFail2(error.Error, "PUT_bad_t", "400 Bad Request",
659 "PUT to a file: bad t=bogus",
660 self.PUT, self.public_url + "/foo/bar.txt?t=bogus",
664 def test_PUT_NEWFILEURL_no_replace(self):
665 d = self.PUT(self.public_url + "/foo/bar.txt?replace=false",
666 self.NEWFILE_CONTENTS)
667 d.addBoth(self.shouldFail, error.Error, "PUT_NEWFILEURL_no_replace",
669 "There was already a child by that name, and you asked me "
673 def test_PUT_NEWFILEURL_mkdirs(self):
674 d = self.PUT(self.public_url + "/foo/newdir/new.txt", self.NEWFILE_CONTENTS)
676 d.addCallback(self.failUnlessURIMatchesChild, fn, u"newdir/new.txt")
677 d.addCallback(lambda res: self.failIfNodeHasChild(fn, u"new.txt"))
678 d.addCallback(lambda res: self.failUnlessNodeHasChild(fn, u"newdir"))
679 d.addCallback(lambda res:
680 self.failUnlessChildContentsAre(fn, u"newdir/new.txt",
681 self.NEWFILE_CONTENTS))
684 def test_PUT_NEWFILEURL_blocked(self):
685 d = self.PUT(self.public_url + "/foo/blockingfile/new.txt",
686 self.NEWFILE_CONTENTS)
687 d.addBoth(self.shouldFail, error.Error, "PUT_NEWFILEURL_blocked",
689 "Unable to create directory 'blockingfile': a file was in the way")
692 def test_DELETE_FILEURL(self):
693 d = self.DELETE(self.public_url + "/foo/bar.txt")
694 d.addCallback(lambda res:
695 self.failIfNodeHasChild(self._foo_node, u"bar.txt"))
698 def test_DELETE_FILEURL_missing(self):
699 d = self.DELETE(self.public_url + "/foo/missing")
700 d.addBoth(self.should404, "test_DELETE_FILEURL_missing")
703 def test_DELETE_FILEURL_missing2(self):
704 d = self.DELETE(self.public_url + "/missing/missing")
705 d.addBoth(self.should404, "test_DELETE_FILEURL_missing2")
708 def test_GET_FILEURL_json(self):
709 # twisted.web.http.parse_qs ignores any query args without an '=', so
710 # I can't do "GET /path?json", I have to do "GET /path/t=json"
711 # instead. This may make it tricky to emulate the S3 interface
713 d = self.GET(self.public_url + "/foo/bar.txt?t=json")
714 d.addCallback(self.failUnlessIsBarJSON)
717 def test_GET_FILEURL_json_missing(self):
718 d = self.GET(self.public_url + "/foo/missing?json")
719 d.addBoth(self.should404, "test_GET_FILEURL_json_missing")
722 def test_GET_FILEURL_uri(self):
723 d = self.GET(self.public_url + "/foo/bar.txt?t=uri")
725 self.failUnlessEqual(res, self._bar_txt_uri)
726 d.addCallback(_check)
727 d.addCallback(lambda res:
728 self.GET(self.public_url + "/foo/bar.txt?t=readonly-uri"))
730 # for now, for files, uris and readonly-uris are the same
731 self.failUnlessEqual(res, self._bar_txt_uri)
732 d.addCallback(_check2)
735 def test_GET_FILEURL_badtype(self):
736 d = self.shouldHTTPError2("GET t=bogus", 400, "Bad Request",
739 self.public_url + "/foo/bar.txt?t=bogus")
742 def test_GET_FILEURL_uri_missing(self):
743 d = self.GET(self.public_url + "/foo/missing?t=uri")
744 d.addBoth(self.should404, "test_GET_FILEURL_uri_missing")
747 def test_GET_DIRURL(self):
748 # the addSlash means we get a redirect here
749 # from /uri/$URI/foo/ , we need ../../../ to get back to the root
751 d = self.GET(self.public_url + "/foo", followRedirect=True)
753 self.failUnless(('<a href="%s">Return to Welcome page' % ROOT)
755 # the FILE reference points to a URI, but it should end in bar.txt
756 bar_url = ("%s/file/%s/@@named=/bar.txt" %
757 (ROOT, urllib.quote(self._bar_txt_uri)))
758 get_bar = "".join([r'<td>',
759 r'<a href="%s">bar.txt</a>' % bar_url,
762 r'\s+<td>%d</td>' % len(self.BAR_CONTENTS),
764 self.failUnless(re.search(get_bar, res), res)
765 for line in res.split("\n"):
766 # find the line that contains the delete button for bar.txt
767 if ("form action" in line and
768 'value="delete"' in line and
769 'value="bar.txt"' in line):
770 # the form target should use a relative URL
771 foo_url = urllib.quote("%s/uri/%s/" % (ROOT, self._foo_uri))
772 self.failUnless(('action="%s"' % foo_url) in line, line)
773 # and the when_done= should too
774 #done_url = urllib.quote(???)
775 #self.failUnless(('name="when_done" value="%s"' % done_url)
779 self.fail("unable to find delete-bar.txt line", res)
781 # the DIR reference just points to a URI
782 sub_url = ("%s/uri/%s/" % (ROOT, urllib.quote(self._sub_uri)))
783 get_sub = ((r'<td><a href="%s">sub</a></td>' % sub_url)
784 + r'\s+<td>DIR</td>')
785 self.failUnless(re.search(get_sub, res), res)
786 d.addCallback(_check)
788 # look at a directory which is readonly
789 d.addCallback(lambda res:
790 self.GET(self.public_url + "/reedownlee", followRedirect=True))
792 self.failUnless("(readonly)" in res, res)
793 self.failIf("Upload a file" in res, res)
794 d.addCallback(_check2)
796 # and at a directory that contains a readonly directory
797 d.addCallback(lambda res:
798 self.GET(self.public_url, followRedirect=True))
800 self.failUnless(re.search(r'<td><a href="[\.\/]+/uri/URI%3ADIR2-RO%3A[^"]+">reedownlee</a>'
801 '</td>\s+<td>DIR-RO</td>', res))
802 d.addCallback(_check3)
806 def test_GET_DIRURL_badtype(self):
807 d = self.shouldHTTPError2("test_GET_DIRURL_badtype",
811 self.public_url + "/foo?t=bogus")
814 def test_GET_DIRURL_json(self):
815 d = self.GET(self.public_url + "/foo?t=json")
816 d.addCallback(self.failUnlessIsFooJSON)
819 def test_GET_DIRURL_manifest(self):
820 d = self.GET(self.public_url + "/foo?t=manifest", followRedirect=True)
822 self.failUnless("Refresh Capabilities" in manifest)
826 def test_GET_DIRURL_deepsize(self):
827 d = self.GET(self.public_url + "/foo?t=deep-size", followRedirect=True)
829 self.failUnless(re.search(r'^\d+$', manifest), manifest)
830 self.failUnlessEqual("57", manifest)
834 def test_GET_DIRURL_deepstats(self):
835 d = self.GET(self.public_url + "/foo?t=deep-stats", followRedirect=True)
836 def _got(stats_json):
837 stats = simplejson.loads(stats_json)
838 expected = {"count-immutable-files": 3,
839 "count-mutable-files": 0,
840 "count-literal-files": 0,
842 "count-directories": 3,
843 "size-immutable-files": 57,
844 "size-literal-files": 0,
845 #"size-directories": 1912, # varies
846 #"largest-directory": 1590,
847 "largest-directory-children": 5,
848 "largest-immutable-file": 19,
850 for k,v in expected.iteritems():
851 self.failUnlessEqual(stats[k], v,
852 "stats[%s] was %s, not %s" %
854 self.failUnlessEqual(stats["size-files-histogram"],
859 def test_GET_DIRURL_uri(self):
860 d = self.GET(self.public_url + "/foo?t=uri")
862 self.failUnlessEqual(res, self._foo_uri)
863 d.addCallback(_check)
866 def test_GET_DIRURL_readonly_uri(self):
867 d = self.GET(self.public_url + "/foo?t=readonly-uri")
869 self.failUnlessEqual(res, self._foo_readonly_uri)
870 d.addCallback(_check)
873 def test_PUT_NEWDIRURL(self):
874 d = self.PUT(self.public_url + "/foo/newdir?t=mkdir", "")
875 d.addCallback(lambda res:
876 self.failUnlessNodeHasChild(self._foo_node, u"newdir"))
877 d.addCallback(lambda res: self._foo_node.get(u"newdir"))
878 d.addCallback(self.failUnlessNodeKeysAre, [])
881 def test_PUT_NEWDIRURL_exists(self):
882 d = self.PUT(self.public_url + "/foo/sub?t=mkdir", "")
883 d.addCallback(lambda res:
884 self.failUnlessNodeHasChild(self._foo_node, u"sub"))
885 d.addCallback(lambda res: self._foo_node.get(u"sub"))
886 d.addCallback(self.failUnlessNodeKeysAre, [u"baz.txt"])
889 def test_PUT_NEWDIRURL_blocked(self):
890 d = self.shouldFail2(error.Error, "PUT_NEWDIRURL_blocked",
891 "409 Conflict", "Unable to create directory 'bar.txt': a file was in the way",
893 self.public_url + "/foo/bar.txt/sub?t=mkdir", "")
894 d.addCallback(lambda res:
895 self.failUnlessNodeHasChild(self._foo_node, u"sub"))
896 d.addCallback(lambda res: self._foo_node.get(u"sub"))
897 d.addCallback(self.failUnlessNodeKeysAre, [u"baz.txt"])
900 def test_PUT_NEWDIRURL_mkdir_p(self):
901 d = defer.succeed(None)
902 d.addCallback(lambda res: self.POST(self.public_url + "/foo", t='mkdir', name='mkp'))
903 d.addCallback(lambda res: self.failUnlessNodeHasChild(self._foo_node, u"mkp"))
904 d.addCallback(lambda res: self._foo_node.get(u"mkp"))
905 def mkdir_p(mkpnode):
906 url = '/uri/%s?t=mkdir-p&path=/sub1/sub2' % urllib.quote(mkpnode.get_uri())
908 def made_subsub(ssuri):
909 d = self._foo_node.get_child_at_path(u"mkp/sub1/sub2")
910 d.addCallback(lambda ssnode: self.failUnlessEqual(ssnode.get_uri(), ssuri))
912 d.addCallback(lambda uri2: self.failUnlessEqual(uri2, ssuri))
914 d.addCallback(made_subsub)
916 d.addCallback(mkdir_p)
919 def test_PUT_NEWDIRURL_mkdirs(self):
920 d = self.PUT(self.public_url + "/foo/subdir/newdir?t=mkdir", "")
921 d.addCallback(lambda res:
922 self.failIfNodeHasChild(self._foo_node, u"newdir"))
923 d.addCallback(lambda res:
924 self.failUnlessNodeHasChild(self._foo_node, u"subdir"))
925 d.addCallback(lambda res:
926 self._foo_node.get_child_at_path(u"subdir/newdir"))
927 d.addCallback(self.failUnlessNodeKeysAre, [])
930 def test_DELETE_DIRURL(self):
931 d = self.DELETE(self.public_url + "/foo")
932 d.addCallback(lambda res:
933 self.failIfNodeHasChild(self.public_root, u"foo"))
936 def test_DELETE_DIRURL_missing(self):
937 d = self.DELETE(self.public_url + "/foo/missing")
938 d.addBoth(self.should404, "test_DELETE_DIRURL_missing")
939 d.addCallback(lambda res:
940 self.failUnlessNodeHasChild(self.public_root, u"foo"))
943 def test_DELETE_DIRURL_missing2(self):
944 d = self.DELETE(self.public_url + "/missing")
945 d.addBoth(self.should404, "test_DELETE_DIRURL_missing2")
950 w = webish.DirnodeWalkerMixin()
951 def visitor(childpath, childnode, metadata):
953 d = w.walk(self.public_root, visitor)
956 def failUnlessNodeKeysAre(self, node, expected_keys):
957 for k in expected_keys:
958 assert isinstance(k, unicode)
960 def _check(children):
961 self.failUnlessEqual(sorted(children.keys()), sorted(expected_keys))
962 d.addCallback(_check)
964 def failUnlessNodeHasChild(self, node, name):
965 assert isinstance(name, unicode)
967 def _check(children):
968 self.failUnless(name in children)
969 d.addCallback(_check)
971 def failIfNodeHasChild(self, node, name):
972 assert isinstance(name, unicode)
974 def _check(children):
975 self.failIf(name in children)
976 d.addCallback(_check)
979 def failUnlessChildContentsAre(self, node, name, expected_contents):
980 assert isinstance(name, unicode)
981 d = node.get_child_at_path(name)
982 d.addCallback(lambda node: node.download_to_data())
983 def _check(contents):
984 self.failUnlessEqual(contents, expected_contents)
985 d.addCallback(_check)
988 def failUnlessMutableChildContentsAre(self, node, name, expected_contents):
989 assert isinstance(name, unicode)
990 d = node.get_child_at_path(name)
991 d.addCallback(lambda node: node.download_best_version())
992 def _check(contents):
993 self.failUnlessEqual(contents, expected_contents)
994 d.addCallback(_check)
997 def failUnlessChildURIIs(self, node, name, expected_uri):
998 assert isinstance(name, unicode)
999 d = node.get_child_at_path(name)
1001 self.failUnlessEqual(child.get_uri(), expected_uri.strip())
1002 d.addCallback(_check)
1005 def failUnlessURIMatchesChild(self, got_uri, node, name):
1006 assert isinstance(name, unicode)
1007 d = node.get_child_at_path(name)
1009 self.failUnlessEqual(got_uri.strip(), child.get_uri())
1010 d.addCallback(_check)
1013 def failUnlessCHKURIHasContents(self, got_uri, contents):
1014 self.failUnless(FakeCHKFileNode.all_contents[got_uri] == contents)
1016 def test_POST_upload(self):
1017 d = self.POST(self.public_url + "/foo", t="upload",
1018 file=("new.txt", self.NEWFILE_CONTENTS))
1020 d.addCallback(self.failUnlessURIMatchesChild, fn, u"new.txt")
1021 d.addCallback(lambda res:
1022 self.failUnlessChildContentsAre(fn, u"new.txt",
1023 self.NEWFILE_CONTENTS))
1026 def test_POST_upload_unicode(self):
1027 filename = u"n\u00e9wer.txt" # n e-acute w e r . t x t
1028 d = self.POST(self.public_url + "/foo", t="upload",
1029 file=(filename, self.NEWFILE_CONTENTS))
1031 d.addCallback(self.failUnlessURIMatchesChild, fn, filename)
1032 d.addCallback(lambda res:
1033 self.failUnlessChildContentsAre(fn, filename,
1034 self.NEWFILE_CONTENTS))
1035 target_url = self.public_url + "/foo/" + filename.encode("utf-8")
1036 d.addCallback(lambda res: self.GET(target_url))
1037 d.addCallback(lambda contents: self.failUnlessEqual(contents,
1038 self.NEWFILE_CONTENTS,
1042 def test_POST_upload_unicode_named(self):
1043 filename = u"n\u00e9wer.txt" # n e-acute w e r . t x t
1044 d = self.POST(self.public_url + "/foo", t="upload",
1046 file=("overridden", self.NEWFILE_CONTENTS))
1048 d.addCallback(self.failUnlessURIMatchesChild, fn, filename)
1049 d.addCallback(lambda res:
1050 self.failUnlessChildContentsAre(fn, filename,
1051 self.NEWFILE_CONTENTS))
1052 target_url = self.public_url + "/foo/" + filename.encode("utf-8")
1053 d.addCallback(lambda res: self.GET(target_url))
1054 d.addCallback(lambda contents: self.failUnlessEqual(contents,
1055 self.NEWFILE_CONTENTS,
1059 def test_POST_upload_no_link(self):
1060 d = self.POST("/uri", t="upload",
1061 file=("new.txt", self.NEWFILE_CONTENTS))
1062 def _check_upload_results(page):
1063 # this should be a page which describes the results of the upload
1064 # that just finished.
1065 self.failUnless("Upload Results:" in page)
1066 self.failUnless("URI:" in page)
1067 uri_re = re.compile("URI: <tt><span>(.*)</span>")
1068 mo = uri_re.search(page)
1069 self.failUnless(mo, page)
1070 new_uri = mo.group(1)
1072 d.addCallback(_check_upload_results)
1073 d.addCallback(self.failUnlessCHKURIHasContents, self.NEWFILE_CONTENTS)
1076 def test_POST_upload_no_link_whendone(self):
1077 d = self.POST("/uri", t="upload", when_done="/",
1078 file=("new.txt", self.NEWFILE_CONTENTS))
1079 d.addBoth(self.shouldRedirect, "/")
1082 def shouldRedirect2(self, which, checker, callable, *args, **kwargs):
1083 d = defer.maybeDeferred(callable, *args, **kwargs)
1085 if isinstance(res, failure.Failure):
1086 res.trap(error.PageRedirect)
1087 statuscode = res.value.status
1088 target = res.value.location
1089 return checker(statuscode, target)
1090 self.fail("%s: callable was supposed to redirect, not return '%s'"
1095 def test_POST_upload_no_link_whendone_results(self):
1096 def check(statuscode, target):
1097 self.failUnlessEqual(statuscode, str(http.FOUND))
1098 self.failUnless(target.startswith(self.webish_url), target)
1099 return client.getPage(target, method="GET")
1100 d = self.shouldRedirect2("test_POST_upload_no_link_whendone_results",
1102 self.POST, "/uri", t="upload",
1103 when_done="/uri/%(uri)s",
1104 file=("new.txt", self.NEWFILE_CONTENTS))
1105 d.addCallback(lambda res:
1106 self.failUnlessEqual(res, self.NEWFILE_CONTENTS))
1109 def test_POST_upload_no_link_mutable(self):
1110 d = self.POST("/uri", t="upload", mutable="true",
1111 file=("new.txt", self.NEWFILE_CONTENTS))
1112 def _check(new_uri):
1113 new_uri = new_uri.strip()
1114 self.new_uri = new_uri
1116 self.failUnless(IMutableFileURI.providedBy(u))
1117 self.failUnless(u.storage_index in FakeMutableFileNode.all_contents)
1118 n = self.s.create_node_from_uri(new_uri)
1119 return n.download_best_version()
1120 d.addCallback(_check)
1122 self.failUnlessEqual(data, self.NEWFILE_CONTENTS)
1123 return self.GET("/uri/%s" % urllib.quote(self.new_uri))
1124 d.addCallback(_check2)
1126 self.failUnlessEqual(data, self.NEWFILE_CONTENTS)
1127 return self.GET("/file/%s" % urllib.quote(self.new_uri))
1128 d.addCallback(_check3)
1130 self.failUnlessEqual(data, self.NEWFILE_CONTENTS)
1131 d.addCallback(_check4)
1134 def test_POST_upload_no_link_mutable_toobig(self):
1135 d = self.shouldFail2(error.Error,
1136 "test_POST_upload_no_link_mutable_toobig",
1137 "413 Request Entity Too Large",
1138 "SDMF is limited to one segment, and 10001 > 10000",
1140 "/uri", t="upload", mutable="true",
1142 "b" * (self.s.MUTABLE_SIZELIMIT+1)) )
1145 def test_POST_upload_mutable(self):
1146 # this creates a mutable file
1147 d = self.POST(self.public_url + "/foo", t="upload", mutable="true",
1148 file=("new.txt", self.NEWFILE_CONTENTS))
1150 d.addCallback(self.failUnlessURIMatchesChild, fn, u"new.txt")
1151 d.addCallback(lambda res:
1152 self.failUnlessMutableChildContentsAre(fn, u"new.txt",
1153 self.NEWFILE_CONTENTS))
1154 d.addCallback(lambda res: self._foo_node.get(u"new.txt"))
1156 self.failUnless(IMutableFileNode.providedBy(newnode))
1157 self.failUnless(newnode.is_mutable())
1158 self.failIf(newnode.is_readonly())
1159 self._mutable_node = newnode
1160 self._mutable_uri = newnode.get_uri()
1163 # now upload it again and make sure that the URI doesn't change
1164 NEWER_CONTENTS = self.NEWFILE_CONTENTS + "newer\n"
1165 d.addCallback(lambda res:
1166 self.POST(self.public_url + "/foo", t="upload",
1168 file=("new.txt", NEWER_CONTENTS)))
1169 d.addCallback(self.failUnlessURIMatchesChild, fn, u"new.txt")
1170 d.addCallback(lambda res:
1171 self.failUnlessMutableChildContentsAre(fn, u"new.txt",
1173 d.addCallback(lambda res: self._foo_node.get(u"new.txt"))
1175 self.failUnless(IMutableFileNode.providedBy(newnode))
1176 self.failUnless(newnode.is_mutable())
1177 self.failIf(newnode.is_readonly())
1178 self.failUnlessEqual(self._mutable_uri, newnode.get_uri())
1179 d.addCallback(_got2)
1181 # upload a second time, using PUT instead of POST
1182 NEW2_CONTENTS = NEWER_CONTENTS + "overwrite with PUT\n"
1183 d.addCallback(lambda res:
1184 self.PUT(self.public_url + "/foo/new.txt", NEW2_CONTENTS))
1185 d.addCallback(self.failUnlessURIMatchesChild, fn, u"new.txt")
1186 d.addCallback(lambda res:
1187 self.failUnlessMutableChildContentsAre(fn, u"new.txt",
1190 # finally list the directory, since mutable files are displayed
1193 d.addCallback(lambda res:
1194 self.GET(self.public_url + "/foo/",
1195 followRedirect=True))
1196 def _check_page(res):
1197 # TODO: assert more about the contents
1198 self.failUnless("Overwrite" in res)
1199 self.failUnless("Choose new file:" in res)
1201 d.addCallback(_check_page)
1203 # test that clicking on the "overwrite" button works
1204 EVEN_NEWER_CONTENTS = NEWER_CONTENTS + "even newer\n"
1205 def _parse_overwrite_form_and_submit(res):
1207 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)
1208 mo = OVERWRITE_FORM_RE.search(res)
1209 self.failUnless(mo, "overwrite form not found in '" + res +
1210 "', in which the overwrite form was not found")
1211 formaction=mo.group(1)
1212 formwhendone=mo.group(2)
1214 fileurl = "../../../uri/" + urllib.quote(self._mutable_uri)
1215 self.failUnlessEqual(formaction, fileurl)
1216 # to POST, we need to absoluteify the URL
1217 new_formaction = "/uri/%s" % urllib.quote(self._mutable_uri)
1218 self.failUnlessEqual(formwhendone,
1219 "../uri/%s/" % urllib.quote(self._foo_uri))
1220 return self.POST(new_formaction,
1222 file=("new.txt", EVEN_NEWER_CONTENTS),
1223 when_done=formwhendone,
1224 followRedirect=False)
1225 d.addCallback(_parse_overwrite_form_and_submit)
1226 # This will redirect us to ../uri/$FOOURI, rather than
1227 # ../uri/$PARENT/foo, but apparently twisted.web.client absolutifies
1228 # the redirect for us, and remember that shouldRedirect prepends
1229 # self.webish_url for us.
1230 d.addBoth(self.shouldRedirect,
1231 "/uri/%s/" % urllib.quote(self._foo_uri),
1232 which="test_POST_upload_mutable.overwrite")
1233 d.addCallback(lambda res:
1234 self.failUnlessMutableChildContentsAre(fn, u"new.txt",
1235 EVEN_NEWER_CONTENTS))
1236 d.addCallback(lambda res: self._foo_node.get(u"new.txt"))
1238 self.failUnless(IMutableFileNode.providedBy(newnode))
1239 self.failUnless(newnode.is_mutable())
1240 self.failIf(newnode.is_readonly())
1241 self.failUnlessEqual(self._mutable_uri, newnode.get_uri())
1242 d.addCallback(_got3)
1244 # look at the JSON form of the enclosing directory
1245 d.addCallback(lambda res:
1246 self.GET(self.public_url + "/foo/?t=json",
1247 followRedirect=True))
1248 def _check_page_json(res):
1249 parsed = simplejson.loads(res)
1250 self.failUnlessEqual(parsed[0], "dirnode")
1251 children = parsed[1]["children"]
1252 self.failUnless("new.txt" in children)
1253 new_json = children["new.txt"]
1254 self.failUnlessEqual(new_json[0], "filenode")
1255 self.failUnless(new_json[1]["mutable"])
1256 self.failUnlessEqual(new_json[1]["rw_uri"], self._mutable_uri)
1257 ro_uri = unicode(self._mutable_node.get_readonly().to_string())
1258 self.failUnlessEqual(new_json[1]["ro_uri"], ro_uri)
1259 d.addCallback(_check_page_json)
1261 # and the JSON form of the file
1262 d.addCallback(lambda res:
1263 self.GET(self.public_url + "/foo/new.txt?t=json"))
1264 def _check_file_json(res):
1265 parsed = simplejson.loads(res)
1266 self.failUnlessEqual(parsed[0], "filenode")
1267 self.failUnless(parsed[1]["mutable"])
1268 self.failUnlessEqual(parsed[1]["rw_uri"], self._mutable_uri)
1269 ro_uri = unicode(self._mutable_node.get_readonly().to_string())
1270 self.failUnlessEqual(parsed[1]["ro_uri"], ro_uri)
1271 d.addCallback(_check_file_json)
1273 # and look at t=uri and t=readonly-uri
1274 d.addCallback(lambda res:
1275 self.GET(self.public_url + "/foo/new.txt?t=uri"))
1276 d.addCallback(lambda res: self.failUnlessEqual(res, self._mutable_uri))
1277 d.addCallback(lambda res:
1278 self.GET(self.public_url + "/foo/new.txt?t=readonly-uri"))
1279 def _check_ro_uri(res):
1280 ro_uri = unicode(self._mutable_node.get_readonly().to_string())
1281 self.failUnlessEqual(res, ro_uri)
1282 d.addCallback(_check_ro_uri)
1284 # make sure we can get to it from /uri/URI
1285 d.addCallback(lambda res:
1286 self.GET("/uri/%s" % urllib.quote(self._mutable_uri)))
1287 d.addCallback(lambda res:
1288 self.failUnlessEqual(res, EVEN_NEWER_CONTENTS))
1290 # and that HEAD computes the size correctly
1291 d.addCallback(lambda res:
1292 self.HEAD(self.public_url + "/foo/new.txt"))
1293 def _got_headers(headers):
1294 self.failUnlessEqual(headers["content-length"][0],
1295 str(len(EVEN_NEWER_CONTENTS)))
1296 self.failUnlessEqual(headers["content-type"], ["text/plain"])
1297 d.addCallback(_got_headers)
1299 # make sure that size errors are displayed correctly for overwrite
1300 d.addCallback(lambda res:
1301 self.shouldFail2(error.Error,
1302 "test_POST_upload_mutable-toobig",
1303 "413 Request Entity Too Large",
1304 "SDMF is limited to one segment, and 10001 > 10000",
1306 self.public_url + "/foo", t="upload",
1309 "b" * (self.s.MUTABLE_SIZELIMIT+1)),
1312 d.addErrback(self.dump_error)
1315 def test_POST_upload_mutable_toobig(self):
1316 d = self.shouldFail2(error.Error,
1317 "test_POST_upload_no_link_mutable_toobig",
1318 "413 Request Entity Too Large",
1319 "SDMF is limited to one segment, and 10001 > 10000",
1321 self.public_url + "/foo",
1322 t="upload", mutable="true",
1324 "b" * (self.s.MUTABLE_SIZELIMIT+1)) )
1327 def dump_error(self, f):
1328 # if the web server returns an error code (like 400 Bad Request),
1329 # web.client.getPage puts the HTTP response body into the .response
1330 # attribute of the exception object that it gives back. It does not
1331 # appear in the Failure's repr(), so the ERROR that trial displays
1332 # will be rather terse and unhelpful. addErrback this method to the
1333 # end of your chain to get more information out of these errors.
1334 if f.check(error.Error):
1335 print "web.error.Error:"
1337 print f.value.response
1340 def test_POST_upload_replace(self):
1341 d = self.POST(self.public_url + "/foo", t="upload",
1342 file=("bar.txt", self.NEWFILE_CONTENTS))
1344 d.addCallback(self.failUnlessURIMatchesChild, fn, u"bar.txt")
1345 d.addCallback(lambda res:
1346 self.failUnlessChildContentsAre(fn, u"bar.txt",
1347 self.NEWFILE_CONTENTS))
1350 def test_POST_upload_no_replace_ok(self):
1351 d = self.POST(self.public_url + "/foo?replace=false", t="upload",
1352 file=("new.txt", self.NEWFILE_CONTENTS))
1353 d.addCallback(lambda res: self.GET(self.public_url + "/foo/new.txt"))
1354 d.addCallback(lambda res: self.failUnlessEqual(res,
1355 self.NEWFILE_CONTENTS))
1358 def test_POST_upload_no_replace_queryarg(self):
1359 d = self.POST(self.public_url + "/foo?replace=false", t="upload",
1360 file=("bar.txt", self.NEWFILE_CONTENTS))
1361 d.addBoth(self.shouldFail, error.Error,
1362 "POST_upload_no_replace_queryarg",
1364 "There was already a child by that name, and you asked me "
1365 "to not replace it")
1366 d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt"))
1367 d.addCallback(self.failUnlessIsBarDotTxt)
1370 def test_POST_upload_no_replace_field(self):
1371 d = self.POST(self.public_url + "/foo", t="upload", replace="false",
1372 file=("bar.txt", self.NEWFILE_CONTENTS))
1373 d.addBoth(self.shouldFail, error.Error, "POST_upload_no_replace_field",
1375 "There was already a child by that name, and you asked me "
1376 "to not replace it")
1377 d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt"))
1378 d.addCallback(self.failUnlessIsBarDotTxt)
1381 def test_POST_upload_whendone(self):
1382 d = self.POST(self.public_url + "/foo", t="upload", when_done="/THERE",
1383 file=("new.txt", self.NEWFILE_CONTENTS))
1384 d.addBoth(self.shouldRedirect, "/THERE")
1386 d.addCallback(lambda res:
1387 self.failUnlessChildContentsAre(fn, u"new.txt",
1388 self.NEWFILE_CONTENTS))
1391 def test_POST_upload_named(self):
1393 d = self.POST(self.public_url + "/foo", t="upload",
1394 name="new.txt", file=self.NEWFILE_CONTENTS)
1395 d.addCallback(self.failUnlessURIMatchesChild, fn, u"new.txt")
1396 d.addCallback(lambda res:
1397 self.failUnlessChildContentsAre(fn, u"new.txt",
1398 self.NEWFILE_CONTENTS))
1401 def test_POST_upload_named_badfilename(self):
1402 d = self.POST(self.public_url + "/foo", t="upload",
1403 name="slashes/are/bad.txt", file=self.NEWFILE_CONTENTS)
1404 d.addBoth(self.shouldFail, error.Error,
1405 "test_POST_upload_named_badfilename",
1407 "name= may not contain a slash",
1409 # make sure that nothing was added
1410 d.addCallback(lambda res:
1411 self.failUnlessNodeKeysAre(self._foo_node,
1412 [u"bar.txt", u"blockingfile",
1413 u"empty", u"n\u00fc.txt",
1417 def test_POST_FILEURL_check(self):
1418 bar_url = self.public_url + "/foo/bar.txt"
1419 d = self.POST(bar_url, t="check")
1421 self.failUnless("Healthy!" in res)
1422 d.addCallback(_check)
1423 redir_url = "http://allmydata.org/TARGET"
1424 def _check2(statuscode, target):
1425 self.failUnlessEqual(statuscode, str(http.FOUND))
1426 self.failUnlessEqual(target, redir_url)
1427 d.addCallback(lambda res:
1428 self.shouldRedirect2("test_POST_FILEURL_check",
1432 when_done=redir_url))
1433 d.addCallback(lambda res:
1434 self.POST(bar_url, t="check", return_to=redir_url))
1436 self.failUnless("Healthy!" in res)
1437 self.failUnless("Return to parent directory" in res)
1438 self.failUnless(redir_url in res)
1439 d.addCallback(_check3)
1442 def test_POST_DIRURL_check(self):
1443 foo_url = self.public_url + "/foo/"
1444 d = self.POST(foo_url, t="check")
1446 self.failUnless("Healthy!" in res)
1447 d.addCallback(_check)
1448 redir_url = "http://allmydata.org/TARGET"
1449 def _check2(statuscode, target):
1450 self.failUnlessEqual(statuscode, str(http.FOUND))
1451 self.failUnlessEqual(target, redir_url)
1452 d.addCallback(lambda res:
1453 self.shouldRedirect2("test_POST_DIRURL_check",
1457 when_done=redir_url))
1458 d.addCallback(lambda res:
1459 self.POST(foo_url, t="check", return_to=redir_url))
1461 self.failUnless("Healthy!" in res)
1462 self.failUnless("Return to parent directory" in res)
1463 self.failUnless(redir_url in res)
1464 d.addCallback(_check3)
1467 def test_POST_DIRURL_deepcheck(self):
1468 d = self.POST(self.public_url, t="deep-check")
1470 self.failUnless("Objects Checked: <span>8</span>" in res)
1471 self.failUnless("Objects Healthy: <span>8</span>" in res)
1472 self.failUnless("Repairs Attempted: <span>0</span>" in res)
1473 self.failUnless("Repairs Successful: <span>0</span>" in res)
1474 d.addCallback(_check)
1475 redir_url = "http://allmydata.org/TARGET"
1476 def _check2(statuscode, target):
1477 self.failUnlessEqual(statuscode, str(http.FOUND))
1478 self.failUnlessEqual(target, redir_url)
1479 d.addCallback(lambda res:
1480 self.shouldRedirect2("test_POST_DIRURL_check",
1482 self.POST, self.public_url,
1484 when_done=redir_url))
1485 d.addCallback(lambda res:
1486 self.POST(self.public_url, t="deep-check",
1487 return_to=redir_url))
1489 self.failUnless("Return to parent directory" in res)
1490 self.failUnless(redir_url in res)
1491 d.addCallback(_check3)
1494 def test_POST_FILEURL_bad_t(self):
1495 d = self.shouldFail2(error.Error, "POST_bad_t", "400 Bad Request",
1496 "POST to file: bad t=bogus",
1497 self.POST, self.public_url + "/foo/bar.txt",
1501 def test_POST_mkdir(self): # return value?
1502 d = self.POST(self.public_url + "/foo", t="mkdir", name="newdir")
1503 d.addCallback(lambda res: self._foo_node.get(u"newdir"))
1504 d.addCallback(self.failUnlessNodeKeysAre, [])
1507 def test_POST_mkdir_2(self):
1508 d = self.POST(self.public_url + "/foo/newdir?t=mkdir", "")
1509 d.addCallback(lambda res:
1510 self.failUnlessNodeHasChild(self._foo_node, u"newdir"))
1511 d.addCallback(lambda res: self._foo_node.get(u"newdir"))
1512 d.addCallback(self.failUnlessNodeKeysAre, [])
1515 def test_POST_mkdirs_2(self):
1516 d = self.POST(self.public_url + "/foo/bardir/newdir?t=mkdir", "")
1517 d.addCallback(lambda res:
1518 self.failUnlessNodeHasChild(self._foo_node, u"bardir"))
1519 d.addCallback(lambda res: self._foo_node.get(u"bardir"))
1520 d.addCallback(lambda bardirnode: bardirnode.get(u"newdir"))
1521 d.addCallback(self.failUnlessNodeKeysAre, [])
1524 def test_POST_mkdir_no_parentdir_noredirect(self):
1525 d = self.POST("/uri?t=mkdir")
1526 def _after_mkdir(res):
1527 uri.NewDirectoryURI.init_from_string(res)
1528 d.addCallback(_after_mkdir)
1531 def test_POST_mkdir_no_parentdir_redirect(self):
1532 d = self.POST("/uri?t=mkdir&redirect_to_result=true")
1533 d.addBoth(self.shouldRedirect, None, statuscode='303')
1534 def _check_target(target):
1535 target = urllib.unquote(target)
1536 self.failUnless(target.startswith("uri/URI:DIR2:"), target)
1537 d.addCallback(_check_target)
1540 def test_POST_noparent_bad(self):
1541 d = self.shouldHTTPError2("POST /uri?t=bogus", 400, "Bad Request",
1542 "/uri accepts only PUT, PUT?t=mkdir, "
1543 "POST?t=upload, and POST?t=mkdir",
1544 self.POST, "/uri?t=bogus")
1547 def test_welcome_page_mkdir_button(self):
1548 # Fetch the welcome page.
1550 def _after_get_welcome_page(res):
1551 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)
1552 mo = MKDIR_BUTTON_RE.search(res)
1553 formaction = mo.group(1)
1555 formaname = mo.group(3)
1556 formavalue = mo.group(4)
1557 return (formaction, formt, formaname, formavalue)
1558 d.addCallback(_after_get_welcome_page)
1559 def _after_parse_form(res):
1560 (formaction, formt, formaname, formavalue) = res
1561 return self.POST("/%s?t=%s&%s=%s" % (formaction, formt, formaname, formavalue))
1562 d.addCallback(_after_parse_form)
1563 d.addBoth(self.shouldRedirect, None, statuscode='303')
1566 def test_POST_mkdir_replace(self): # return value?
1567 d = self.POST(self.public_url + "/foo", t="mkdir", name="sub")
1568 d.addCallback(lambda res: self._foo_node.get(u"sub"))
1569 d.addCallback(self.failUnlessNodeKeysAre, [])
1572 def test_POST_mkdir_no_replace_queryarg(self): # return value?
1573 d = self.POST(self.public_url + "/foo?replace=false", t="mkdir", name="sub")
1574 d.addBoth(self.shouldFail, error.Error,
1575 "POST_mkdir_no_replace_queryarg",
1577 "There was already a child by that name, and you asked me "
1578 "to not replace it")
1579 d.addCallback(lambda res: self._foo_node.get(u"sub"))
1580 d.addCallback(self.failUnlessNodeKeysAre, [u"baz.txt"])
1583 def test_POST_mkdir_no_replace_field(self): # return value?
1584 d = self.POST(self.public_url + "/foo", t="mkdir", name="sub",
1586 d.addBoth(self.shouldFail, error.Error, "POST_mkdir_no_replace_field",
1588 "There was already a child by that name, and you asked me "
1589 "to not replace it")
1590 d.addCallback(lambda res: self._foo_node.get(u"sub"))
1591 d.addCallback(self.failUnlessNodeKeysAre, [u"baz.txt"])
1594 def test_POST_mkdir_whendone_field(self):
1595 d = self.POST(self.public_url + "/foo",
1596 t="mkdir", name="newdir", when_done="/THERE")
1597 d.addBoth(self.shouldRedirect, "/THERE")
1598 d.addCallback(lambda res: self._foo_node.get(u"newdir"))
1599 d.addCallback(self.failUnlessNodeKeysAre, [])
1602 def test_POST_mkdir_whendone_queryarg(self):
1603 d = self.POST(self.public_url + "/foo?when_done=/THERE",
1604 t="mkdir", name="newdir")
1605 d.addBoth(self.shouldRedirect, "/THERE")
1606 d.addCallback(lambda res: self._foo_node.get(u"newdir"))
1607 d.addCallback(self.failUnlessNodeKeysAre, [])
1610 def test_POST_bad_t(self):
1611 d = self.shouldFail2(error.Error, "POST_bad_t", "400 Bad Request",
1612 "POST to a directory with bad t=BOGUS",
1613 self.POST, self.public_url + "/foo", t="BOGUS")
1616 def test_POST_set_children(self):
1617 contents9, n9, newuri9 = self.makefile(9)
1618 contents10, n10, newuri10 = self.makefile(10)
1619 contents11, n11, newuri11 = self.makefile(11)
1622 "atomic_added_1": [ "filenode", { "rw_uri": "%s",
1625 "ctime": 1002777696.7564139,
1626 "mtime": 1002777696.7564139
1629 "atomic_added_2": [ "filenode", { "rw_uri": "%s",
1632 "ctime": 1002777696.7564139,
1633 "mtime": 1002777696.7564139
1636 "atomic_added_3": [ "filenode", { "rw_uri": "%s",
1639 "ctime": 1002777696.7564139,
1640 "mtime": 1002777696.7564139
1643 }""" % (newuri9, newuri10, newuri11)
1645 url = self.webish_url + self.public_url + "/foo" + "?t=set_children"
1647 d = client.getPage(url, method="POST", postdata=reqbody)
1649 self.failUnlessURIMatchesChild(newuri9, self._foo_node, u"atomic_added_1")
1650 self.failUnlessURIMatchesChild(newuri10, self._foo_node, u"atomic_added_2")
1651 self.failUnlessURIMatchesChild(newuri11, self._foo_node, u"atomic_added_3")
1653 d.addCallback(_then)
1654 d.addErrback(self.dump_error)
1657 def test_POST_put_uri(self):
1658 contents, n, newuri = self.makefile(8)
1659 d = self.POST(self.public_url + "/foo", t="uri", name="new.txt", uri=newuri)
1660 d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"new.txt")
1661 d.addCallback(lambda res:
1662 self.failUnlessChildContentsAre(self._foo_node, u"new.txt",
1666 def test_POST_put_uri_replace(self):
1667 contents, n, newuri = self.makefile(8)
1668 d = self.POST(self.public_url + "/foo", t="uri", name="bar.txt", uri=newuri)
1669 d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"bar.txt")
1670 d.addCallback(lambda res:
1671 self.failUnlessChildContentsAre(self._foo_node, u"bar.txt",
1675 def test_POST_put_uri_no_replace_queryarg(self):
1676 contents, n, newuri = self.makefile(8)
1677 d = self.POST(self.public_url + "/foo?replace=false", t="uri",
1678 name="bar.txt", uri=newuri)
1679 d.addBoth(self.shouldFail, error.Error,
1680 "POST_put_uri_no_replace_queryarg",
1682 "There was already a child by that name, and you asked me "
1683 "to not replace it")
1684 d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt"))
1685 d.addCallback(self.failUnlessIsBarDotTxt)
1688 def test_POST_put_uri_no_replace_field(self):
1689 contents, n, newuri = self.makefile(8)
1690 d = self.POST(self.public_url + "/foo", t="uri", replace="false",
1691 name="bar.txt", uri=newuri)
1692 d.addBoth(self.shouldFail, error.Error,
1693 "POST_put_uri_no_replace_field",
1695 "There was already a child by that name, and you asked me "
1696 "to not replace it")
1697 d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt"))
1698 d.addCallback(self.failUnlessIsBarDotTxt)
1701 def test_POST_delete(self):
1702 d = self.POST(self.public_url + "/foo", t="delete", name="bar.txt")
1703 d.addCallback(lambda res: self._foo_node.list())
1704 def _check(children):
1705 self.failIf(u"bar.txt" in children)
1706 d.addCallback(_check)
1709 def test_POST_rename_file(self):
1710 d = self.POST(self.public_url + "/foo", t="rename",
1711 from_name="bar.txt", to_name='wibble.txt')
1712 d.addCallback(lambda res:
1713 self.failIfNodeHasChild(self._foo_node, u"bar.txt"))
1714 d.addCallback(lambda res:
1715 self.failUnlessNodeHasChild(self._foo_node, u"wibble.txt"))
1716 d.addCallback(lambda res: self.GET(self.public_url + "/foo/wibble.txt"))
1717 d.addCallback(self.failUnlessIsBarDotTxt)
1718 d.addCallback(lambda res: self.GET(self.public_url + "/foo/wibble.txt?t=json"))
1719 d.addCallback(self.failUnlessIsBarJSON)
1722 def test_POST_rename_file_replace(self):
1723 # rename a file and replace a directory with it
1724 d = self.POST(self.public_url + "/foo", t="rename",
1725 from_name="bar.txt", to_name='empty')
1726 d.addCallback(lambda res:
1727 self.failIfNodeHasChild(self._foo_node, u"bar.txt"))
1728 d.addCallback(lambda res:
1729 self.failUnlessNodeHasChild(self._foo_node, u"empty"))
1730 d.addCallback(lambda res: self.GET(self.public_url + "/foo/empty"))
1731 d.addCallback(self.failUnlessIsBarDotTxt)
1732 d.addCallback(lambda res: self.GET(self.public_url + "/foo/empty?t=json"))
1733 d.addCallback(self.failUnlessIsBarJSON)
1736 def test_POST_rename_file_no_replace_queryarg(self):
1737 # rename a file and replace a directory with it
1738 d = self.POST(self.public_url + "/foo?replace=false", t="rename",
1739 from_name="bar.txt", to_name='empty')
1740 d.addBoth(self.shouldFail, error.Error,
1741 "POST_rename_file_no_replace_queryarg",
1743 "There was already a child by that name, and you asked me "
1744 "to not replace it")
1745 d.addCallback(lambda res: self.GET(self.public_url + "/foo/empty?t=json"))
1746 d.addCallback(self.failUnlessIsEmptyJSON)
1749 def test_POST_rename_file_no_replace_field(self):
1750 # rename a file and replace a directory with it
1751 d = self.POST(self.public_url + "/foo", t="rename", replace="false",
1752 from_name="bar.txt", to_name='empty')
1753 d.addBoth(self.shouldFail, error.Error,
1754 "POST_rename_file_no_replace_field",
1756 "There was already a child by that name, and you asked me "
1757 "to not replace it")
1758 d.addCallback(lambda res: self.GET(self.public_url + "/foo/empty?t=json"))
1759 d.addCallback(self.failUnlessIsEmptyJSON)
1762 def failUnlessIsEmptyJSON(self, res):
1763 data = simplejson.loads(res)
1764 self.failUnlessEqual(data[0], "dirnode", data)
1765 self.failUnlessEqual(len(data[1]["children"]), 0)
1767 def test_POST_rename_file_slash_fail(self):
1768 d = self.POST(self.public_url + "/foo", t="rename",
1769 from_name="bar.txt", to_name='kirk/spock.txt')
1770 d.addBoth(self.shouldFail, error.Error,
1771 "test_POST_rename_file_slash_fail",
1773 "to_name= may not contain a slash",
1775 d.addCallback(lambda res:
1776 self.failUnlessNodeHasChild(self._foo_node, u"bar.txt"))
1777 d.addCallback(lambda res: self.POST(self.public_url, t="rename",
1778 from_name="foo/bar.txt", to_name='george.txt'))
1779 d.addBoth(self.shouldFail, error.Error,
1780 "test_POST_rename_file_slash_fail",
1782 "from_name= may not contain a slash",
1784 d.addCallback(lambda res:
1785 self.failUnlessNodeHasChild(self.public_root, u"foo"))
1786 d.addCallback(lambda res:
1787 self.failIfNodeHasChild(self.public_root, u"george.txt"))
1788 d.addCallback(lambda res:
1789 self.failUnlessNodeHasChild(self._foo_node, u"bar.txt"))
1790 d.addCallback(lambda res: self.GET(self.public_url + "/foo?t=json"))
1791 d.addCallback(self.failUnlessIsFooJSON)
1794 def test_POST_rename_dir(self):
1795 d = self.POST(self.public_url, t="rename",
1796 from_name="foo", to_name='plunk')
1797 d.addCallback(lambda res:
1798 self.failIfNodeHasChild(self.public_root, u"foo"))
1799 d.addCallback(lambda res:
1800 self.failUnlessNodeHasChild(self.public_root, u"plunk"))
1801 d.addCallback(lambda res: self.GET(self.public_url + "/plunk?t=json"))
1802 d.addCallback(self.failUnlessIsFooJSON)
1805 def shouldRedirect(self, res, target=None, statuscode=None, which=""):
1806 """ If target is not None then the redirection has to go to target. If
1807 statuscode is not None then the redirection has to be accomplished with
1808 that HTTP status code."""
1809 if not isinstance(res, failure.Failure):
1810 to_where = (target is None) and "somewhere" or ("to " + target)
1811 self.fail("%s: we were expecting to get redirected %s, not get an"
1812 " actual page: %s" % (which, to_where, res))
1813 res.trap(error.PageRedirect)
1814 if statuscode is not None:
1815 self.failUnlessEqual(res.value.status, statuscode,
1816 "%s: not a redirect" % which)
1817 if target is not None:
1818 # the PageRedirect does not seem to capture the uri= query arg
1819 # properly, so we can't check for it.
1820 realtarget = self.webish_url + target
1821 self.failUnlessEqual(res.value.location, realtarget,
1822 "%s: wrong target" % which)
1823 return res.value.location
1825 def test_GET_URI_form(self):
1826 base = "/uri?uri=%s" % self._bar_txt_uri
1827 # this is supposed to give us a redirect to /uri/$URI, plus arguments
1828 targetbase = "/uri/%s" % urllib.quote(self._bar_txt_uri)
1830 d.addBoth(self.shouldRedirect, targetbase)
1831 d.addCallback(lambda res: self.GET(base+"&filename=bar.txt"))
1832 d.addBoth(self.shouldRedirect, targetbase+"?filename=bar.txt")
1833 d.addCallback(lambda res: self.GET(base+"&t=json"))
1834 d.addBoth(self.shouldRedirect, targetbase+"?t=json")
1835 d.addCallback(self.log, "about to get file by uri")
1836 d.addCallback(lambda res: self.GET(base, followRedirect=True))
1837 d.addCallback(self.failUnlessIsBarDotTxt)
1838 d.addCallback(self.log, "got file by uri, about to get dir by uri")
1839 d.addCallback(lambda res: self.GET("/uri?uri=%s&t=json" % self._foo_uri,
1840 followRedirect=True))
1841 d.addCallback(self.failUnlessIsFooJSON)
1842 d.addCallback(self.log, "got dir by uri")
1846 def test_GET_URI_form_bad(self):
1847 d = self.shouldFail2(error.Error, "test_GET_URI_form_bad",
1848 "400 Bad Request", "GET /uri requires uri=",
1852 def test_GET_rename_form(self):
1853 d = self.GET(self.public_url + "/foo?t=rename-form&name=bar.txt",
1854 followRedirect=True)
1856 self.failUnless('name="when_done" value="."' in res, res)
1857 self.failUnless(re.search(r'name="from_name" value="bar\.txt"', res))
1858 d.addCallback(_check)
1861 def log(self, res, msg):
1862 #print "MSG: %s RES: %s" % (msg, res)
1866 def test_GET_URI_URL(self):
1867 base = "/uri/%s" % self._bar_txt_uri
1869 d.addCallback(self.failUnlessIsBarDotTxt)
1870 d.addCallback(lambda res: self.GET(base+"?filename=bar.txt"))
1871 d.addCallback(self.failUnlessIsBarDotTxt)
1872 d.addCallback(lambda res: self.GET(base+"?filename=bar.txt&save=true"))
1873 d.addCallback(self.failUnlessIsBarDotTxt)
1876 def test_GET_URI_URL_dir(self):
1877 base = "/uri/%s?t=json" % self._foo_uri
1879 d.addCallback(self.failUnlessIsFooJSON)
1882 def test_GET_URI_URL_missing(self):
1883 base = "/uri/%s" % self._bad_file_uri
1885 d.addBoth(self.shouldHTTPError, "test_GET_URI_URL_missing",
1886 http.GONE, response_substring="NotEnoughSharesError")
1887 # TODO: how can we exercise both sides of WebDownloadTarget.fail
1888 # here? we must arrange for a download to fail after target.open()
1889 # has been called, and then inspect the response to see that it is
1890 # shorter than we expected.
1893 def test_PUT_NEWFILEURL_uri(self):
1894 contents, n, new_uri = self.makefile(8)
1895 d = self.PUT(self.public_url + "/foo/new.txt?t=uri", new_uri)
1896 d.addCallback(lambda res: self.failUnlessEqual(res.strip(), new_uri))
1897 d.addCallback(lambda res:
1898 self.failUnlessChildContentsAre(self._foo_node, u"new.txt",
1902 def test_PUT_NEWFILEURL_uri_replace(self):
1903 contents, n, new_uri = self.makefile(8)
1904 d = self.PUT(self.public_url + "/foo/bar.txt?t=uri", new_uri)
1905 d.addCallback(lambda res: self.failUnlessEqual(res.strip(), new_uri))
1906 d.addCallback(lambda res:
1907 self.failUnlessChildContentsAre(self._foo_node, u"bar.txt",
1911 def test_PUT_NEWFILEURL_uri_no_replace(self):
1912 contents, n, new_uri = self.makefile(8)
1913 d = self.PUT(self.public_url + "/foo/bar.txt?t=uri&replace=false", new_uri)
1914 d.addBoth(self.shouldFail, error.Error, "PUT_NEWFILEURL_uri_no_replace",
1916 "There was already a child by that name, and you asked me "
1917 "to not replace it")
1920 def test_PUT_NEWFILE_URI(self):
1921 file_contents = "New file contents here\n"
1922 d = self.PUT("/uri", file_contents)
1924 self.failUnless(uri in FakeCHKFileNode.all_contents)
1925 self.failUnlessEqual(FakeCHKFileNode.all_contents[uri],
1927 return self.GET("/uri/%s" % uri)
1928 d.addCallback(_check)
1930 self.failUnlessEqual(res, file_contents)
1931 d.addCallback(_check2)
1934 def test_PUT_NEWFILE_URI_only_PUT(self):
1935 d = self.PUT("/uri?t=bogus", "")
1936 d.addBoth(self.shouldFail, error.Error,
1937 "PUT_NEWFILE_URI_only_PUT",
1939 "/uri accepts only PUT, PUT?t=mkdir, POST?t=upload, and POST?t=mkdir")
1942 def test_PUT_NEWFILE_URI_mutable(self):
1943 file_contents = "New file contents here\n"
1944 d = self.PUT("/uri?mutable=true", file_contents)
1945 def _check_mutable(uri):
1948 self.failUnless(IMutableFileURI.providedBy(u))
1949 self.failUnless(u.storage_index in FakeMutableFileNode.all_contents)
1950 n = self.s.create_node_from_uri(uri)
1951 return n.download_best_version()
1952 d.addCallback(_check_mutable)
1953 def _check2_mutable(data):
1954 self.failUnlessEqual(data, file_contents)
1955 d.addCallback(_check2_mutable)
1959 self.failUnless(uri in FakeCHKFileNode.all_contents)
1960 self.failUnlessEqual(FakeCHKFileNode.all_contents[uri],
1962 return self.GET("/uri/%s" % uri)
1963 d.addCallback(_check)
1965 self.failUnlessEqual(res, file_contents)
1966 d.addCallback(_check2)
1969 def test_PUT_mkdir(self):
1970 d = self.PUT("/uri?t=mkdir", "")
1972 n = self.s.create_node_from_uri(uri.strip())
1973 d2 = self.failUnlessNodeKeysAre(n, [])
1974 d2.addCallback(lambda res:
1975 self.GET("/uri/%s?t=json" % uri))
1977 d.addCallback(_check)
1978 d.addCallback(self.failUnlessIsEmptyJSON)
1981 def test_POST_check(self):
1982 d = self.POST(self.public_url + "/foo", t="check", name="bar.txt")
1984 # this returns a string form of the results, which are probably
1985 # None since we're using fake filenodes.
1986 # TODO: verify that the check actually happened, by changing
1987 # FakeCHKFileNode to count how many times .check() has been
1990 d.addCallback(_done)
1993 def test_bad_method(self):
1994 url = self.webish_url + self.public_url + "/foo/bar.txt"
1995 d = self.shouldHTTPError2("test_bad_method",
1996 501, "Not Implemented",
1997 "I don't know how to treat a BOGUS request.",
1998 client.getPage, url, method="BOGUS")
2001 def test_short_url(self):
2002 url = self.webish_url + "/uri"
2003 d = self.shouldHTTPError2("test_short_url", 501, "Not Implemented",
2004 "I don't know how to treat a DELETE request.",
2005 client.getPage, url, method="DELETE")
2008 class Util(unittest.TestCase):
2009 def test_abbreviate_time(self):
2010 self.failUnlessEqual(common.abbreviate_time(None), "")
2011 self.failUnlessEqual(common.abbreviate_time(1.234), "1.23s")
2012 self.failUnlessEqual(common.abbreviate_time(0.123), "123ms")
2013 self.failUnlessEqual(common.abbreviate_time(0.00123), "1.2ms")
2014 self.failUnlessEqual(common.abbreviate_time(0.000123), "123us")
2016 def test_abbreviate_rate(self):
2017 self.failUnlessEqual(common.abbreviate_rate(None), "")
2018 self.failUnlessEqual(common.abbreviate_rate(1234000), "1.23MBps")
2019 self.failUnlessEqual(common.abbreviate_rate(12340), "12.3kBps")
2020 self.failUnlessEqual(common.abbreviate_rate(123), "123Bps")
2022 def test_abbreviate_size(self):
2023 self.failUnlessEqual(common.abbreviate_size(None), "")
2024 self.failUnlessEqual(common.abbreviate_size(1.23*1000*1000*1000), "1.23GB")
2025 self.failUnlessEqual(common.abbreviate_size(1.23*1000*1000), "1.23MB")
2026 self.failUnlessEqual(common.abbreviate_size(1230), "1.2kB")
2027 self.failUnlessEqual(common.abbreviate_size(123), "123B")