1 import re, os.path, urllib
3 from twisted.application import service
4 from twisted.trial import unittest
5 from twisted.internet import defer
6 from twisted.web import client, error, http
7 from twisted.python import failure, log
8 from allmydata import interfaces, provisioning, uri, webish, upload, download
9 from allmydata.web import status
10 from allmydata.util import fileutil
11 from allmydata.test.common import FakeDirectoryNode, FakeCHKFileNode, FakeMutableFileNode, create_chk_filenode
12 from allmydata.interfaces import IURI, INewDirectoryURI, IReadonlyNewDirectoryURI, IFileURI, IMutableFileURI, IMutableFileNode
14 # create a fake uploader/downloader, and a couple of fake dirnodes, then
15 # create a webserver that works against them
17 class FakeIntroducerClient:
18 def get_all_connectors(self):
20 def get_all_connections_for(self, service_name):
22 def get_all_peerids(self):
25 class FakeClient(service.MultiService):
26 nodeid = "fake_nodeid"
27 basedir = "fake_basedir"
28 def get_versions(self):
29 return {'allmydata': "fake",
34 introducer_furl = "None"
35 introducer_client = FakeIntroducerClient()
36 _all_upload_status = [upload.UploadStatus()]
37 _all_download_status = [download.DownloadStatus()]
38 convergence = "some random string"
40 def connected_to_introducer(self):
43 def create_node_from_uri(self, auri):
44 u = uri.from_string(auri)
45 if (INewDirectoryURI.providedBy(u)
46 or IReadonlyNewDirectoryURI.providedBy(u)):
47 return FakeDirectoryNode(self).init_from_uri(u)
48 if IFileURI.providedBy(u):
49 return FakeCHKFileNode(u, self)
50 assert IMutableFileURI.providedBy(u), u
51 return FakeMutableFileNode(self).init_from_uri(u)
53 def create_empty_dirnode(self):
54 n = FakeDirectoryNode(self)
56 d.addCallback(lambda res: n)
59 def create_mutable_file(self, contents=""):
60 n = FakeMutableFileNode(self)
61 return n.create(contents)
63 def upload(self, uploadable):
64 d = uploadable.get_size()
65 d.addCallback(lambda size: uploadable.read(size))
68 n = create_chk_filenode(self, data)
69 results = upload.UploadResults()
70 results.uri = n.get_uri()
72 d.addCallback(_got_data)
75 def list_all_uploads(self):
77 def list_all_downloads(self):
80 def list_active_uploads(self):
81 return self._all_upload_status
82 def list_active_downloads(self):
83 return self._all_download_status
84 def list_active_publish(self):
86 def list_active_retrieve(self):
89 def list_recent_uploads(self):
90 return self._all_upload_status
91 def list_recent_downloads(self):
92 return self._all_download_status
93 def list_recent_publish(self):
95 def list_recent_retrieve(self):
97 def list_active_helper_statuses(self):
99 def list_recent_helper_statuses(self):
102 class WebMixin(object):
104 self.s = FakeClient()
105 self.s.startService()
106 self.ws = s = webish.WebishServer("0")
107 s.allow_local_access(True)
108 s.setServiceParent(self.s)
109 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 foo.set_uri(u"sub", sub_uri)
134 sub = self.s.create_node_from_uri(sub_uri)
136 _ign, n, blocking_uri = self.makefile(1)
137 foo.set_uri(u"blockingfile", blocking_uri)
139 unicode_filename = u"n\u00fc.txt" # n u-umlaut . t x t
140 # ok, unicode calls it LATIN SMALL LETTER U WITH DIAERESIS but I
141 # still think of it as an umlaut
142 foo.set_uri(unicode_filename, self._bar_txt_uri)
144 _ign, n, baz_file = self.makefile(2)
145 sub.set_uri(u"baz.txt", baz_file)
147 _ign, n, self._bad_file_uri = self.makefile(3)
148 # this uri should not be downloadable
149 del FakeCHKFileNode.all_contents[self._bad_file_uri]
152 self.public_root.set_uri(u"reedownlee", rodir.get_readonly_uri())
153 rodir.set_uri(u"nor", baz_file)
158 # public/foo/blockingfile
161 # public/foo/sub/baz.txt
163 # public/reedownlee/nor
164 self.NEWFILE_CONTENTS = "newfile contents\n"
166 return foo.get_metadata_for(u"bar.txt")
168 def _got_metadata(metadata):
169 self._bar_txt_metadata = metadata
170 d.addCallback(_got_metadata)
173 def makefile(self, number):
174 contents = "contents of file %s\n" % number
175 n = create_chk_filenode(self.s, contents)
176 return contents, n, n.get_uri()
179 return self.s.stopService()
181 def failUnlessIsBarDotTxt(self, res):
182 self.failUnlessEqual(res, self.BAR_CONTENTS, res)
184 def failUnlessIsBarJSON(self, res):
185 data = simplejson.loads(res)
186 self.failUnless(isinstance(data, list))
187 self.failUnlessEqual(data[0], "filenode")
188 self.failUnless(isinstance(data[1], dict))
189 self.failIf("rw_uri" in data[1]) # immutable
190 self.failUnlessEqual(data[1]["ro_uri"], self._bar_txt_uri)
191 self.failUnlessEqual(data[1]["size"], len(self.BAR_CONTENTS))
193 def failUnlessIsFooJSON(self, res):
194 data = simplejson.loads(res)
195 self.failUnless(isinstance(data, list))
196 self.failUnlessEqual(data[0], "dirnode", res)
197 self.failUnless(isinstance(data[1], dict))
198 self.failUnless("rw_uri" in data[1]) # mutable
199 self.failUnlessEqual(data[1]["rw_uri"], self._foo_uri)
200 self.failUnlessEqual(data[1]["ro_uri"], self._foo_readonly_uri)
202 kidnames = sorted(data[1]["children"])
203 self.failUnlessEqual(kidnames,
204 [u"bar.txt", u"blockingfile", u"empty",
205 u"n\u00fc.txt", u"sub"])
206 kids = data[1]["children"]
207 self.failUnlessEqual(kids[u"sub"][0], "dirnode")
208 self.failUnless("metadata" in kids[u"sub"][1])
209 self.failUnless("ctime" in kids[u"sub"][1]["metadata"])
210 self.failUnless("mtime" in kids[u"sub"][1]["metadata"])
211 self.failUnlessEqual(kids[u"bar.txt"][0], "filenode")
212 self.failUnlessEqual(kids[u"bar.txt"][1]["size"], len(self.BAR_CONTENTS))
213 self.failUnlessEqual(kids[u"bar.txt"][1]["ro_uri"], self._bar_txt_uri)
214 self.failUnlessEqual(kids[u"bar.txt"][1]["metadata"]["ctime"],
215 self._bar_txt_metadata["ctime"])
216 self.failUnlessEqual(kids[u"n\u00fc.txt"][1]["ro_uri"],
219 def GET(self, urlpath, followRedirect=False):
220 url = self.webish_url + urlpath
221 return client.getPage(url, method="GET", followRedirect=followRedirect)
223 def PUT(self, urlpath, data):
224 url = self.webish_url + urlpath
225 return client.getPage(url, method="PUT", postdata=data)
227 def DELETE(self, urlpath):
228 url = self.webish_url + urlpath
229 return client.getPage(url, method="DELETE")
231 def POST(self, urlpath, followRedirect=False, **fields):
232 url = self.webish_url + urlpath
233 sepbase = "boogabooga"
237 form.append('Content-Disposition: form-data; name="_charset"')
241 for name, value in fields.iteritems():
242 if isinstance(value, tuple):
243 filename, value = value
244 form.append('Content-Disposition: form-data; name="%s"; '
245 'filename="%s"' % (name, filename.encode("utf-8")))
247 form.append('Content-Disposition: form-data; name="%s"' % name)
249 form.append(str(value))
252 body = "\r\n".join(form) + "\r\n"
253 headers = {"content-type": "multipart/form-data; boundary=%s" % sepbase,
255 return client.getPage(url, method="POST", postdata=body,
256 headers=headers, followRedirect=followRedirect)
258 def shouldFail(self, res, expected_failure, which,
259 substring=None, response_substring=None):
260 if isinstance(res, failure.Failure):
261 res.trap(expected_failure)
263 self.failUnless(substring in str(res),
264 "substring '%s' not in '%s'"
265 % (substring, str(res)))
266 if response_substring:
267 self.failUnless(response_substring in res.value.response,
268 "respose substring '%s' not in '%s'"
269 % (response_substring, res.value.response))
271 self.fail("%s was supposed to raise %s, not get '%s'" %
272 (which, expected_failure, res))
274 def shouldFail2(self, expected_failure, which, substring,
276 callable, *args, **kwargs):
277 assert substring is None or isinstance(substring, str)
278 assert response_substring is None or isinstance(response_substring, str)
279 d = defer.maybeDeferred(callable, *args, **kwargs)
281 if isinstance(res, failure.Failure):
282 res.trap(expected_failure)
284 self.failUnless(substring in str(res),
285 "substring '%s' not in '%s'"
286 % (substring, str(res)))
287 if response_substring:
288 self.failUnless(response_substring in res.value.response,
289 "respose substring '%s' not in '%s'"
290 % (response_substring, res.value.response))
292 self.fail("%s was supposed to raise %s, not get '%s'" %
293 (which, expected_failure, res))
297 def should404(self, res, which):
298 if isinstance(res, failure.Failure):
299 res.trap(error.Error)
300 self.failUnlessEqual(res.value.status, "404")
302 self.fail("%s was supposed to Error(404), not get '%s'" %
305 def shouldHTTPError(self, res, which, code=None, substring=None,
306 response_substring=None):
307 if isinstance(res, failure.Failure):
308 res.trap(error.Error)
310 self.failUnlessEqual(res.value.status, str(code))
312 self.failUnless(substring in str(res),
313 "substring '%s' not in '%s'"
314 % (substring, str(res)))
315 if response_substring:
316 self.failUnless(response_substring in res.value.response,
317 "respose substring '%s' not in '%s'"
318 % (response_substring, res.value.response))
320 self.fail("%s was supposed to Error(%s), not get '%s'" %
323 def shouldHTTPError2(self, which,
324 code=None, substring=None, response_substring=None,
325 callable=None, *args, **kwargs):
326 assert substring is None or isinstance(substring, str)
328 d = defer.maybeDeferred(callable, *args, **kwargs)
329 d.addBoth(self.shouldHTTPError, which,
330 code, substring, response_substring)
334 class Web(WebMixin, unittest.TestCase):
335 def test_create(self):
338 def test_welcome(self):
341 self.failUnless('Welcome To AllMyData' in res)
342 self.failUnless('Tahoe' in res)
344 self.s.basedir = 'web/test_welcome'
345 fileutil.make_dirs("web/test_welcome")
346 fileutil.make_dirs("web/test_welcome/private")
348 d.addCallback(_check)
351 def test_provisioning_math(self):
352 self.failUnlessEqual(provisioning.binomial(10, 0), 1)
353 self.failUnlessEqual(provisioning.binomial(10, 1), 10)
354 self.failUnlessEqual(provisioning.binomial(10, 2), 45)
355 self.failUnlessEqual(provisioning.binomial(10, 9), 10)
356 self.failUnlessEqual(provisioning.binomial(10, 10), 1)
358 def test_provisioning(self):
359 d = self.GET("/provisioning/")
361 self.failUnless('Tahoe Provisioning Tool' in res)
362 fields = {'filled': True,
363 "num_users": int(50e3),
364 "files_per_user": 1000,
365 "space_per_user": int(1e9),
366 "sharing_ratio": 1.0,
367 "encoding_parameters": "3-of-10-5",
369 "ownership_mode": "A",
370 "download_rate": 100,
375 return self.POST("/provisioning/", **fields)
377 d.addCallback(_check)
379 self.failUnless('Tahoe Provisioning Tool' in res)
380 self.failUnless("Share space consumed: 167.01TB" in res)
382 fields = {'filled': True,
383 "num_users": int(50e6),
384 "files_per_user": 1000,
385 "space_per_user": int(5e9),
386 "sharing_ratio": 1.0,
387 "encoding_parameters": "25-of-100-50",
388 "num_servers": 30000,
389 "ownership_mode": "E",
390 "drive_failure_model": "U",
392 "download_rate": 1000,
397 return self.POST("/provisioning/", **fields)
398 d.addCallback(_check2)
400 self.failUnless("Share space consumed: huge!" in res)
401 fields = {'filled': True}
402 return self.POST("/provisioning/", **fields)
403 d.addCallback(_check3)
405 self.failUnless("Share space consumed:" in res)
406 d.addCallback(_check4)
409 def test_status(self):
410 dl_num = self.s.list_recent_downloads()[0].get_counter()
411 ul_num = self.s.list_recent_uploads()[0].get_counter()
412 d = self.GET("/status", followRedirect=True)
414 self.failUnless('Upload and Download Status' in res, res)
415 self.failUnless('"down-%d"' % dl_num in res, res)
416 self.failUnless('"up-%d"' % ul_num in res, res)
417 d.addCallback(_check)
418 d.addCallback(lambda res: self.GET("/status/down-%d" % dl_num))
420 self.failUnless("File Download Status" in res, res)
421 d.addCallback(_check_dl)
422 d.addCallback(lambda res: self.GET("/status/up-%d" % ul_num))
424 self.failUnless("File Upload Status" in res, res)
425 d.addCallback(_check_ul)
428 def test_status_numbers(self):
429 drrm = status.DownloadResultsRendererMixin()
430 self.failUnlessEqual(drrm.render_time(None, None), "")
431 self.failUnlessEqual(drrm.render_time(None, 2.5), "2.50s")
432 self.failUnlessEqual(drrm.render_time(None, 0.25), "250ms")
433 self.failUnlessEqual(drrm.render_time(None, 0.0021), "2.1ms")
434 self.failUnlessEqual(drrm.render_time(None, 0.000123), "123us")
435 self.failUnlessEqual(drrm.render_rate(None, None), "")
436 self.failUnlessEqual(drrm.render_rate(None, 2500000), "2.50MBps")
437 self.failUnlessEqual(drrm.render_rate(None, 30100), "30.1kBps")
438 self.failUnlessEqual(drrm.render_rate(None, 123), "123Bps")
440 urrm = status.UploadResultsRendererMixin()
441 self.failUnlessEqual(urrm.render_time(None, None), "")
442 self.failUnlessEqual(urrm.render_time(None, 2.5), "2.50s")
443 self.failUnlessEqual(urrm.render_time(None, 0.25), "250ms")
444 self.failUnlessEqual(urrm.render_time(None, 0.0021), "2.1ms")
445 self.failUnlessEqual(urrm.render_time(None, 0.000123), "123us")
446 self.failUnlessEqual(urrm.render_rate(None, None), "")
447 self.failUnlessEqual(urrm.render_rate(None, 2500000), "2.50MBps")
448 self.failUnlessEqual(urrm.render_rate(None, 30100), "30.1kBps")
449 self.failUnlessEqual(urrm.render_rate(None, 123), "123Bps")
451 def test_GET_FILEURL(self):
452 d = self.GET(self.public_url + "/foo/bar.txt")
453 d.addCallback(self.failUnlessIsBarDotTxt)
456 def test_GET_FILEURL_save(self):
457 d = self.GET(self.public_url + "/foo/bar.txt?save=bar.txt")
458 # TODO: look at the headers, expect a Content-Disposition: attachment
460 d.addCallback(self.failUnlessIsBarDotTxt)
463 def test_GET_FILEURL_download(self):
464 d = self.GET(self.public_url + "/foo/bar.txt?t=download")
465 d.addCallback(self.failUnlessIsBarDotTxt)
468 def test_GET_FILEURL_missing(self):
469 d = self.GET(self.public_url + "/foo/missing")
470 d.addBoth(self.should404, "test_GET_FILEURL_missing")
473 def test_PUT_NEWFILEURL(self):
474 d = self.PUT(self.public_url + "/foo/new.txt", self.NEWFILE_CONTENTS)
475 # TODO: we lose the response code, so we can't check this
476 #self.failUnlessEqual(responsecode, 201)
477 d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"new.txt")
478 d.addCallback(lambda res:
479 self.failUnlessChildContentsAre(self._foo_node, u"new.txt",
480 self.NEWFILE_CONTENTS))
483 def test_PUT_NEWFILEURL_replace(self):
484 d = self.PUT(self.public_url + "/foo/bar.txt", self.NEWFILE_CONTENTS)
485 # TODO: we lose the response code, so we can't check this
486 #self.failUnlessEqual(responsecode, 200)
487 d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"bar.txt")
488 d.addCallback(lambda res:
489 self.failUnlessChildContentsAre(self._foo_node, u"bar.txt",
490 self.NEWFILE_CONTENTS))
493 def test_PUT_NEWFILEURL_no_replace(self):
494 d = self.PUT(self.public_url + "/foo/bar.txt?replace=false",
495 self.NEWFILE_CONTENTS)
496 d.addBoth(self.shouldFail, error.Error, "PUT_NEWFILEURL_no_replace",
498 "There was already a child by that name, and you asked me "
502 def test_PUT_NEWFILEURL_mkdirs(self):
503 d = self.PUT(self.public_url + "/foo/newdir/new.txt", self.NEWFILE_CONTENTS)
505 d.addCallback(self.failUnlessURIMatchesChild, fn, u"newdir/new.txt")
506 d.addCallback(lambda res: self.failIfNodeHasChild(fn, u"new.txt"))
507 d.addCallback(lambda res: self.failUnlessNodeHasChild(fn, u"newdir"))
508 d.addCallback(lambda res:
509 self.failUnlessChildContentsAre(fn, u"newdir/new.txt",
510 self.NEWFILE_CONTENTS))
513 def test_PUT_NEWFILEURL_blocked(self):
514 d = self.PUT(self.public_url + "/foo/blockingfile/new.txt",
515 self.NEWFILE_CONTENTS)
516 d.addBoth(self.shouldFail, error.Error, "PUT_NEWFILEURL_blocked",
518 "cannot create directory because there is a file in the way")
521 def test_DELETE_FILEURL(self):
522 d = self.DELETE(self.public_url + "/foo/bar.txt")
523 d.addCallback(lambda res:
524 self.failIfNodeHasChild(self._foo_node, u"bar.txt"))
527 def test_DELETE_FILEURL_missing(self):
528 d = self.DELETE(self.public_url + "/foo/missing")
529 d.addBoth(self.should404, "test_DELETE_FILEURL_missing")
532 def test_DELETE_FILEURL_missing2(self):
533 d = self.DELETE(self.public_url + "/missing/missing")
534 d.addBoth(self.should404, "test_DELETE_FILEURL_missing2")
537 def test_GET_FILEURL_json(self):
538 # twisted.web.http.parse_qs ignores any query args without an '=', so
539 # I can't do "GET /path?json", I have to do "GET /path/t=json"
540 # instead. This may make it tricky to emulate the S3 interface
542 d = self.GET(self.public_url + "/foo/bar.txt?t=json")
543 d.addCallback(self.failUnlessIsBarJSON)
546 def test_GET_FILEURL_json_missing(self):
547 d = self.GET(self.public_url + "/foo/missing?json")
548 d.addBoth(self.should404, "test_GET_FILEURL_json_missing")
551 def disable_local_access(self, res=None):
552 self.ws.allow_local_access(False)
555 def test_GET_FILEURL_localfile(self):
556 localfile = os.path.abspath("web/GET_FILEURL_local file")
557 url = (self.public_url + "/foo/bar.txt?t=download&localfile=%s" %
558 urllib.quote(localfile))
559 fileutil.make_dirs("web")
562 self.failUnless(os.path.exists(localfile))
563 data = open(localfile, "rb").read()
564 self.failUnlessEqual(data, self.BAR_CONTENTS)
568 def test_GET_FILEURL_localfile_disabled(self):
569 localfile = os.path.abspath("web/GET_FILEURL_local file_disabled")
570 url = (self.public_url + "/foo/bar.txt?t=download&localfile=%s" %
571 urllib.quote(localfile))
572 fileutil.make_dirs("web")
573 self.disable_local_access()
575 d.addBoth(self.shouldFail, error.Error, "localfile disabled",
577 "local file access is disabled")
580 def test_GET_FILEURL_localfile_nonlocal(self):
581 # TODO: somehow pretend that we aren't local, and verify that the
582 # server refuses to write to local files, probably by changing the
583 # server's idea of what counts as "local".
584 old_LOCALHOST = webish.LOCALHOST
585 webish.LOCALHOST = "127.0.0.2"
586 localfile = os.path.abspath("web/GET_FILEURL_local file_nonlocal")
587 fileutil.make_dirs("web")
588 d = self.GET(self.public_url + "/foo/bar.txt?t=download&localfile=%s"
589 % urllib.quote(localfile))
590 d.addBoth(self.shouldFail, error.Error, "localfile non-local",
592 "localfile= or localdir= requires a local connection")
594 self.failIf(os.path.exists(localfile))
595 d.addCallback(_check)
597 webish.LOCALHOST = old_LOCALHOST
602 def test_GET_FILEURL_localfile_nonabsolute(self):
603 localfile = "web/nonabsolute/path"
604 fileutil.make_dirs("web/nonabsolute")
605 d = self.GET(self.public_url + "/foo/bar.txt?t=download&localfile=%s"
606 % urllib.quote(localfile))
607 d.addBoth(self.shouldFail, error.Error, "localfile non-absolute",
609 "localfile= or localdir= requires an absolute path")
611 self.failIf(os.path.exists(localfile))
612 d.addCallback(_check)
615 def test_PUT_NEWFILEURL_localfile(self):
616 localfile = os.path.abspath("web/PUT_NEWFILEURL_local file")
617 url = (self.public_url + "/foo/new.txt?t=upload&localfile=%s" %
618 urllib.quote(localfile))
619 fileutil.make_dirs("web")
620 f = open(localfile, "wb")
621 f.write(self.NEWFILE_CONTENTS)
623 d = self.PUT(url, "")
624 d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"new.txt")
625 d.addCallback(lambda res:
626 self.failUnlessChildContentsAre(self._foo_node, u"new.txt",
627 self.NEWFILE_CONTENTS))
630 def test_PUT_NEWFILEURL_localfile_missingarg(self):
631 url = self.public_url + "/foo/new.txt?t=upload"
632 d = self.shouldHTTPError2("test_PUT_NEWFILEURL_localfile_missing",
634 "t=upload requires localfile= or localdir=",
638 def test_PUT_NEWFILEURL_localfile_disabled(self):
639 localfile = os.path.abspath("web/PUT_NEWFILEURL_local file_disabled")
640 url = (self.public_url + "/foo/new.txt?t=upload&localfile=%s" %
641 urllib.quote(localfile))
642 fileutil.make_dirs("web")
643 f = open(localfile, "wb")
644 f.write(self.NEWFILE_CONTENTS)
646 self.disable_local_access()
647 d = self.PUT(url, "")
648 d.addBoth(self.shouldFail, error.Error, "put localfile disabled",
650 "local file access is disabled")
653 def test_PUT_NEWFILEURL_localfile_mkdirs(self):
654 localfile = os.path.abspath("web/PUT_NEWFILEURL_local file_mkdirs")
655 fileutil.make_dirs("web")
656 f = open(localfile, "wb")
657 f.write(self.NEWFILE_CONTENTS)
659 d = self.PUT(self.public_url + "/foo/newdir/new.txt?t=upload&localfile=%s"
660 % urllib.quote(localfile), "")
661 d.addCallback(self.failUnlessURIMatchesChild,
662 self._foo_node, u"newdir/new.txt")
663 d.addCallback(lambda res:
664 self.failIfNodeHasChild(self._foo_node, u"new.txt"))
665 d.addCallback(lambda res:
666 self.failUnlessNodeHasChild(self._foo_node, u"newdir"))
667 d.addCallback(lambda res:
668 self.failUnlessChildContentsAre(self._foo_node,
670 self.NEWFILE_CONTENTS))
673 def test_GET_FILEURL_uri(self):
674 d = self.GET(self.public_url + "/foo/bar.txt?t=uri")
676 self.failUnlessEqual(res, self._bar_txt_uri)
677 d.addCallback(_check)
678 d.addCallback(lambda res:
679 self.GET(self.public_url + "/foo/bar.txt?t=readonly-uri"))
681 # for now, for files, uris and readonly-uris are the same
682 self.failUnlessEqual(res, self._bar_txt_uri)
683 d.addCallback(_check2)
686 def test_GET_FILEURL_badtype(self):
687 d = self.shouldHTTPError2("GET t=bogus", 400, "Bad Request",
690 self.public_url + "/foo/bar.txt?t=bogus")
693 def test_GET_FILEURL_uri_missing(self):
694 d = self.GET(self.public_url + "/foo/missing?t=uri")
695 d.addBoth(self.should404, "test_GET_FILEURL_uri_missing")
698 def test_GET_DIRURL(self):
699 # the addSlash means we get a redirect here
700 d = self.GET(self.public_url + "/foo", followRedirect=True)
702 # the FILE reference points to a URI, but it should end in bar.txt
703 self.failUnless(re.search(r'<td>'
704 '<a href="[^"]+bar.txt">bar.txt</a>'
707 '\s+<td>%d</td>' % len(self.BAR_CONTENTS)
709 # the DIR reference just points to a URI
710 self.failUnless(re.search(r'<td><a href="/uri/URI%3ADIR2%3A[^"]+">sub</a></td>'
711 '\s+<td>DIR</td>', res))
712 d.addCallback(_check)
714 # look at a directory which is readonly
715 d.addCallback(lambda res:
716 self.GET(self.public_url + "/reedownlee", followRedirect=True))
718 self.failUnless("(readonly)" in res, res)
719 self.failIf("Upload a file" in res, res)
720 d.addCallback(_check2)
722 # and at a directory that contains a readonly directory
723 d.addCallback(lambda res:
724 self.GET(self.public_url, followRedirect=True))
726 self.failUnless(re.search(r'<td><a href="/uri/URI%3ADIR2-RO%3A[^"]+">reedownlee</a>'
727 '</td>\s+<td>DIR-RO</td>', res))
728 d.addCallback(_check3)
732 def test_GET_DIRURL_badtype(self):
733 d = self.shouldHTTPError2("test_GET_DIRURL_badtype",
737 self.public_url + "/foo?t=bogus")
740 def test_GET_DIRURL_json(self):
741 d = self.GET(self.public_url + "/foo?t=json")
742 d.addCallback(self.failUnlessIsFooJSON)
745 def test_GET_DIRURL_manifest(self):
746 d = self.GET(self.public_url + "/foo?t=manifest", followRedirect=True)
748 self.failUnless("Refresh Capabilities" in manifest)
752 def test_GET_DIRURL_deepsize(self):
753 d = self.GET(self.public_url + "/foo?t=deep-size", followRedirect=True)
755 self.failUnless(re.search(r'^\d+$', manifest), manifest)
756 self.failUnlessEqual("57", manifest)
760 def test_GET_DIRURL_uri(self):
761 d = self.GET(self.public_url + "/foo?t=uri")
763 self.failUnlessEqual(res, self._foo_uri)
764 d.addCallback(_check)
767 def test_GET_DIRURL_readonly_uri(self):
768 d = self.GET(self.public_url + "/foo?t=readonly-uri")
770 self.failUnlessEqual(res, self._foo_readonly_uri)
771 d.addCallback(_check)
774 def test_PUT_NEWDIRURL(self):
775 d = self.PUT(self.public_url + "/foo/newdir?t=mkdir", "")
776 d.addCallback(lambda res:
777 self.failUnlessNodeHasChild(self._foo_node, u"newdir"))
778 d.addCallback(lambda res: self._foo_node.get(u"newdir"))
779 d.addCallback(self.failUnlessNodeKeysAre, [])
782 def test_PUT_NEWDIRURL_replace(self):
783 d = self.PUT(self.public_url + "/foo/sub?t=mkdir", "")
784 d.addCallback(lambda res:
785 self.failUnlessNodeHasChild(self._foo_node, u"sub"))
786 d.addCallback(lambda res: self._foo_node.get(u"sub"))
787 d.addCallback(self.failUnlessNodeKeysAre, [])
790 def test_PUT_NEWDIRURL_no_replace(self):
791 d = self.PUT(self.public_url + "/foo/sub?t=mkdir&replace=false", "")
792 d.addBoth(self.shouldFail, error.Error, "PUT_NEWDIRURL_no_replace",
794 "There was already a child by that name, and you asked me "
796 d.addCallback(lambda res:
797 self.failUnlessNodeHasChild(self._foo_node, u"sub"))
798 d.addCallback(lambda res: self._foo_node.get(u"sub"))
799 d.addCallback(self.failUnlessNodeKeysAre, [u"baz.txt"])
802 def test_PUT_NEWDIRURL_mkdir_p(self):
803 d = defer.succeed(None)
804 d.addCallback(lambda res: self.POST(self.public_url + "/foo", t='mkdir', name='mkp'))
805 d.addCallback(lambda res: self.failUnlessNodeHasChild(self._foo_node, u"mkp"))
806 d.addCallback(lambda res: self._foo_node.get(u"mkp"))
807 def mkdir_p(mkpnode):
808 url = '/uri/%s?t=mkdir-p&path=/sub1/sub2' % urllib.quote(mkpnode.get_uri())
810 def made_subsub(ssuri):
811 d = self._foo_node.get_child_at_path(u"mkp/sub1/sub2")
812 d.addCallback(lambda ssnode: self.failUnlessEqual(ssnode.get_uri(), ssuri))
814 d.addCallback(lambda uri2: self.failUnlessEqual(uri2, ssuri))
816 d.addCallback(made_subsub)
818 d.addCallback(mkdir_p)
821 def test_PUT_NEWDIRURL_mkdirs(self):
822 d = self.PUT(self.public_url + "/foo/subdir/newdir?t=mkdir", "")
823 d.addCallback(lambda res:
824 self.failIfNodeHasChild(self._foo_node, u"newdir"))
825 d.addCallback(lambda res:
826 self.failUnlessNodeHasChild(self._foo_node, u"subdir"))
827 d.addCallback(lambda res:
828 self._foo_node.get_child_at_path(u"subdir/newdir"))
829 d.addCallback(self.failUnlessNodeKeysAre, [])
832 def test_DELETE_DIRURL(self):
833 d = self.DELETE(self.public_url + "/foo")
834 d.addCallback(lambda res:
835 self.failIfNodeHasChild(self.public_root, u"foo"))
838 def test_DELETE_DIRURL_missing(self):
839 d = self.DELETE(self.public_url + "/foo/missing")
840 d.addBoth(self.should404, "test_DELETE_DIRURL_missing")
841 d.addCallback(lambda res:
842 self.failUnlessNodeHasChild(self.public_root, u"foo"))
845 def test_DELETE_DIRURL_missing2(self):
846 d = self.DELETE(self.public_url + "/missing")
847 d.addBoth(self.should404, "test_DELETE_DIRURL_missing2")
850 def test_walker(self):
852 def _visitor(path, node, metadata):
853 out.append((path, node))
854 return defer.succeed(None)
855 w = webish.DirnodeWalkerMixin()
856 d = w.walk(self.public_root, _visitor)
858 names = [path for (path,node) in out]
859 self.failUnlessEqual(sorted(names),
862 (u'foo',u'blockingfile'),
864 (u'foo', u"n\u00fc.txt"),
866 (u'foo',u'sub',u'baz.txt'),
868 (u'reedownlee', u'nor'),
870 subindex = names.index( (u'foo', u'sub') )
871 bazindex = names.index( (u'foo', u'sub', u'baz.txt') )
872 self.failUnless(subindex < bazindex)
873 for path,node in out:
874 if path[-1] in (u'bar.txt', u"n\u00fc.txt", u'blockingfile',
876 self.failUnless(interfaces.IFileNode.providedBy(node))
878 self.failUnless(interfaces.IDirectoryNode.providedBy(node))
879 d.addCallback(_check)
882 def test_GET_DIRURL_localdir(self):
883 localdir = os.path.abspath("web/GET_DIRURL_local dir")
884 fileutil.make_dirs("web")
885 d = self.GET(self.public_url + "/foo?t=download&localdir=%s" %
886 urllib.quote(localdir))
888 barfile = os.path.join(localdir, "bar.txt")
889 self.failUnless(os.path.exists(barfile))
890 data = open(barfile, "rb").read()
891 self.failUnlessEqual(data, self.BAR_CONTENTS)
892 blockingfile = os.path.join(localdir, "blockingfile")
893 self.failUnless(os.path.exists(blockingfile))
894 subdir = os.path.join(localdir, "sub")
895 self.failUnless(os.path.isdir(subdir))
896 d.addCallback(_check)
899 def test_GET_DIRURL_localdir_disabled(self):
900 localdir = os.path.abspath("web/GET_DIRURL_local dir_disabled")
901 fileutil.make_dirs("web")
902 self.disable_local_access()
903 d = self.GET(self.public_url + "/foo?t=download&localdir=%s" %
904 urllib.quote(localdir))
905 d.addBoth(self.shouldFail, error.Error, "localfile disabled",
907 "local file access is disabled")
910 def test_GET_DIRURL_localdir_nonabsolute(self):
911 localdir = "web/nonabsolute/dir path"
912 fileutil.make_dirs("web/nonabsolute")
913 d = self.GET(self.public_url + "/foo?t=download&localdir=%s" %
914 urllib.quote(localdir))
915 d.addBoth(self.shouldFail, error.Error, "localdir non-absolute",
917 "localfile= or localdir= requires an absolute path")
919 self.failIf(os.path.exists(localdir))
920 d.addCallback(_check)
923 def test_GET_DIRURL_localdir_nolocaldir(self):
924 d = self.shouldHTTPError2("GET_DIRURL_localdir_nolocaldir",
926 "t=download requires localdir=",
928 self.public_url + "/foo?t=download")
931 def touch(self, localdir, filename):
932 path = os.path.join(localdir, filename)
934 f.write("contents of %s\n" % filename)
939 w = webish.DirnodeWalkerMixin()
940 def visitor(childpath, childnode, metadata):
942 d = w.walk(self.public_root, visitor)
945 def failUnlessNodeKeysAre(self, node, expected_keys):
946 for k in expected_keys:
947 assert isinstance(k, unicode)
949 def _check(children):
950 self.failUnlessEqual(sorted(children.keys()), sorted(expected_keys))
951 d.addCallback(_check)
953 def failUnlessNodeHasChild(self, node, name):
954 assert isinstance(name, unicode)
956 def _check(children):
957 self.failUnless(name in children)
958 d.addCallback(_check)
960 def failIfNodeHasChild(self, node, name):
961 assert isinstance(name, unicode)
963 def _check(children):
964 self.failIf(name in children)
965 d.addCallback(_check)
968 def failUnlessChildContentsAre(self, node, name, expected_contents):
969 assert isinstance(name, unicode)
970 d = node.get_child_at_path(name)
971 d.addCallback(lambda node: node.download_to_data())
972 def _check(contents):
973 self.failUnlessEqual(contents, expected_contents)
974 d.addCallback(_check)
977 def failUnlessChildURIIs(self, node, name, expected_uri):
978 assert isinstance(name, unicode)
979 d = node.get_child_at_path(name)
981 self.failUnlessEqual(child.get_uri(), expected_uri.strip())
982 d.addCallback(_check)
985 def failUnlessURIMatchesChild(self, got_uri, node, name):
986 assert isinstance(name, unicode)
987 d = node.get_child_at_path(name)
989 self.failUnlessEqual(got_uri.strip(), child.get_uri())
990 d.addCallback(_check)
993 def failUnlessCHKURIHasContents(self, got_uri, contents):
994 self.failUnless(FakeCHKFileNode.all_contents[got_uri] == contents)
996 def test_PUT_NEWDIRURL_localdir(self):
997 localdir = os.path.abspath("web/PUT_NEWDIRURL_local dir")
998 # create some files there
999 fileutil.make_dirs(os.path.join(localdir, "one"))
1000 fileutil.make_dirs(os.path.join(localdir, "one/sub"))
1001 fileutil.make_dirs(os.path.join(localdir, "two"))
1002 fileutil.make_dirs(os.path.join(localdir, "three"))
1003 self.touch(localdir, "three/foo.txt")
1004 self.touch(localdir, "three/bar.txt")
1005 self.touch(localdir, "zap.zip")
1007 d = self.PUT(self.public_url + "/newdir?t=upload&localdir=%s"
1008 % urllib.quote(localdir), "")
1009 pr = self.public_root
1010 d.addCallback(lambda res: self.failUnlessNodeHasChild(pr, u"newdir"))
1011 d.addCallback(lambda res: pr.get(u"newdir"))
1012 d.addCallback(self.failUnlessNodeKeysAre,
1013 [u"one", u"two", u"three", u"zap.zip"])
1014 d.addCallback(lambda res: pr.get_child_at_path(u"newdir/one"))
1015 d.addCallback(self.failUnlessNodeKeysAre, [u"sub"])
1016 d.addCallback(lambda res: pr.get_child_at_path(u"newdir/three"))
1017 d.addCallback(self.failUnlessNodeKeysAre, [u"foo.txt", u"bar.txt"])
1018 d.addCallback(lambda res: pr.get_child_at_path(u"newdir/three/bar.txt"))
1019 d.addCallback(lambda barnode: barnode.download_to_data())
1020 d.addCallback(lambda contents:
1021 self.failUnlessEqual(contents,
1022 "contents of three/bar.txt\n"))
1025 def test_PUT_NEWDIRURL_localdir_disabled(self):
1026 localdir = os.path.abspath("web/PUT_NEWDIRURL_local dir_disabled")
1027 # create some files there
1028 fileutil.make_dirs(os.path.join(localdir, "one"))
1029 fileutil.make_dirs(os.path.join(localdir, "one/sub"))
1030 fileutil.make_dirs(os.path.join(localdir, "two"))
1031 fileutil.make_dirs(os.path.join(localdir, "three"))
1032 self.touch(localdir, "three/foo.txt")
1033 self.touch(localdir, "three/bar.txt")
1034 self.touch(localdir, "zap.zip")
1036 self.disable_local_access()
1037 d = self.PUT(self.public_url + "/newdir?t=upload&localdir=%s"
1038 % urllib.quote(localdir), "")
1039 d.addBoth(self.shouldFail, error.Error, "localfile disabled",
1041 "local file access is disabled")
1044 def test_PUT_NEWDIRURL_localdir_mkdirs(self):
1045 localdir = os.path.abspath("web/PUT_NEWDIRURL_local dir_mkdirs")
1046 # create some files there
1047 fileutil.make_dirs(os.path.join(localdir, "one"))
1048 fileutil.make_dirs(os.path.join(localdir, "one/sub"))
1049 fileutil.make_dirs(os.path.join(localdir, "two"))
1050 fileutil.make_dirs(os.path.join(localdir, "three"))
1051 self.touch(localdir, "three/foo.txt")
1052 self.touch(localdir, "three/bar.txt")
1053 self.touch(localdir, "zap.zip")
1055 d = self.PUT(self.public_url + "/foo/subdir/newdir?t=upload&localdir=%s"
1056 % urllib.quote(localdir),
1059 d.addCallback(lambda res: self.failUnlessNodeHasChild(fn, u"subdir"))
1060 d.addCallback(lambda res: fn.get_child_at_path(u"subdir/newdir"))
1061 d.addCallback(self.failUnlessNodeKeysAre,
1062 [u"one", u"two", u"three", u"zap.zip"])
1063 d.addCallback(lambda res: fn.get_child_at_path(u"subdir/newdir/one"))
1064 d.addCallback(self.failUnlessNodeKeysAre, [u"sub"])
1065 d.addCallback(lambda res: fn.get_child_at_path(u"subdir/newdir/three"))
1066 d.addCallback(self.failUnlessNodeKeysAre, [u"foo.txt", u"bar.txt"])
1067 d.addCallback(lambda res:
1068 fn.get_child_at_path(u"subdir/newdir/three/bar.txt"))
1069 d.addCallback(lambda barnode: barnode.download_to_data())
1070 d.addCallback(lambda contents:
1071 self.failUnlessEqual(contents,
1072 "contents of three/bar.txt\n"))
1075 def test_PUT_NEWDIRURL_localdir_missing(self):
1076 localdir = os.path.abspath("web/PUT_NEWDIRURL_localdir_missing")
1077 # we do *not* create it, to trigger an error
1078 url = (self.public_url + "/foo/subdir/newdir?t=upload&localdir=%s"
1079 % urllib.quote(localdir))
1080 d = self.shouldHTTPError2("test_PUT_NEWDIRURL_localdir_missing",
1082 "%s doesn't exist!" % localdir,
1086 def test_POST_upload(self):
1087 d = self.POST(self.public_url + "/foo", t="upload",
1088 file=("new.txt", self.NEWFILE_CONTENTS))
1090 d.addCallback(self.failUnlessURIMatchesChild, fn, u"new.txt")
1091 d.addCallback(lambda res:
1092 self.failUnlessChildContentsAre(fn, u"new.txt",
1093 self.NEWFILE_CONTENTS))
1096 def test_POST_upload_unicode(self):
1097 filename = u"n\u00e9wer.txt" # n e-acute w e r . t x t
1098 target_url = self.public_url + "/foo/" + filename.encode("utf-8")
1099 d = self.POST(self.public_url + "/foo", t="upload",
1100 file=(filename, self.NEWFILE_CONTENTS))
1102 d.addCallback(self.failUnlessURIMatchesChild, fn, filename)
1103 d.addCallback(lambda res:
1104 self.failUnlessChildContentsAre(fn, filename,
1105 self.NEWFILE_CONTENTS))
1106 d.addCallback(lambda res: self.GET(target_url))
1107 d.addCallback(lambda contents: self.failUnlessEqual(contents,
1108 self.NEWFILE_CONTENTS,
1112 def test_POST_upload_no_link(self):
1113 d = self.POST("/uri", t="upload",
1114 file=("new.txt", self.NEWFILE_CONTENTS))
1115 def _check_upload_results(page):
1116 # this should be a page which describes the results of the upload
1117 # that just finished.
1118 self.failUnless("Upload Results:" in page)
1119 self.failUnless("URI:" in page)
1120 uri_re = re.compile("URI: <tt><span>(.*)</span>")
1121 mo = uri_re.search(page)
1122 self.failUnless(mo, page)
1123 new_uri = mo.group(1)
1125 d.addCallback(_check_upload_results)
1126 d.addCallback(self.failUnlessCHKURIHasContents, self.NEWFILE_CONTENTS)
1129 def test_POST_upload_no_link_whendone(self):
1130 d = self.POST("/uri", t="upload", when_done="/",
1131 file=("new.txt", self.NEWFILE_CONTENTS))
1132 d.addBoth(self.shouldRedirect, "/")
1135 def test_POST_upload_no_link_mutable(self):
1136 d = self.POST("/uri", t="upload", mutable="true",
1137 file=("new.txt", self.NEWFILE_CONTENTS))
1138 def _check(new_uri):
1139 new_uri = new_uri.strip()
1141 self.failUnless(IMutableFileURI.providedBy(u))
1142 self.failUnless(u.storage_index in FakeMutableFileNode.all_contents)
1143 n = self.s.create_node_from_uri(new_uri)
1144 return n.download_to_data()
1145 d.addCallback(_check)
1147 self.failUnlessEqual(data, self.NEWFILE_CONTENTS)
1148 d.addCallback(_check2)
1151 def test_POST_upload_mutable(self):
1152 # this creates a mutable file
1153 d = self.POST(self.public_url + "/foo", t="upload", mutable="true",
1154 file=("new.txt", self.NEWFILE_CONTENTS))
1156 d.addCallback(self.failUnlessURIMatchesChild, fn, u"new.txt")
1157 d.addCallback(lambda res:
1158 self.failUnlessChildContentsAre(fn, u"new.txt",
1159 self.NEWFILE_CONTENTS))
1160 d.addCallback(lambda res: self._foo_node.get(u"new.txt"))
1162 self.failUnless(IMutableFileNode.providedBy(newnode))
1163 self.failUnless(newnode.is_mutable())
1164 self.failIf(newnode.is_readonly())
1165 self._mutable_uri = newnode.get_uri()
1168 # now upload it again and make sure that the URI doesn't change
1169 NEWER_CONTENTS = self.NEWFILE_CONTENTS + "newer\n"
1170 d.addCallback(lambda res:
1171 self.POST(self.public_url + "/foo", t="upload",
1173 file=("new.txt", NEWER_CONTENTS)))
1174 d.addCallback(self.failUnlessURIMatchesChild, fn, u"new.txt")
1175 d.addCallback(lambda res:
1176 self.failUnlessChildContentsAre(fn, u"new.txt",
1178 d.addCallback(lambda res: self._foo_node.get(u"new.txt"))
1180 self.failUnless(IMutableFileNode.providedBy(newnode))
1181 self.failUnless(newnode.is_mutable())
1182 self.failIf(newnode.is_readonly())
1183 self.failUnlessEqual(self._mutable_uri, newnode.get_uri())
1184 d.addCallback(_got2)
1186 # finally list the directory, since mutable files are displayed
1189 d.addCallback(lambda res:
1190 self.GET(self.public_url + "/foo",
1191 followRedirect=True))
1192 def _check_page(res):
1193 # TODO: assert more about the contents
1194 self.failUnless("Overwrite" in res)
1195 self.failUnless("Choose new file:" in res)
1197 d.addCallback(_check_page)
1199 # test that clicking on the "overwrite" button works
1200 EVEN_NEWER_CONTENTS = NEWER_CONTENTS + "even newer\n"
1201 def _parse_overwrite_form_and_submit(res):
1202 OVERWRITE_FORM_RE=re.compile('<form action="([^"]*)" method="post" .*<input type="hidden" name="t" value="overwrite" /><input type="hidden" name="name" value="([^"]*)" /><input type="hidden" name="when_done" value="([^"]*)" />', re.I)
1203 mo = OVERWRITE_FORM_RE.search(res)
1205 formaction=mo.group(1)
1206 formname=mo.group(2)
1207 formwhendone=mo.group(3)
1209 if formaction == ".":
1210 formaction = self.public_url + "/foo"
1211 return self.POST(formaction, t="overwrite", name=formname, when_done=formwhendone, file=("new.txt", EVEN_NEWER_CONTENTS), followRedirect=False)
1212 d.addCallback(_parse_overwrite_form_and_submit)
1213 d.addBoth(self.shouldRedirect, urllib.quote(self.public_url + "/foo/"))
1214 d.addCallback(lambda res:
1215 self.failUnlessChildContentsAre(fn, u"new.txt",
1216 EVEN_NEWER_CONTENTS))
1217 d.addCallback(lambda res: self._foo_node.get(u"new.txt"))
1219 self.failUnless(IMutableFileNode.providedBy(newnode))
1220 self.failUnless(newnode.is_mutable())
1221 self.failIf(newnode.is_readonly())
1222 self.failUnlessEqual(self._mutable_uri, newnode.get_uri())
1223 d.addCallback(_got3)
1227 def test_POST_upload_replace(self):
1228 d = self.POST(self.public_url + "/foo", t="upload",
1229 file=("bar.txt", self.NEWFILE_CONTENTS))
1231 d.addCallback(self.failUnlessURIMatchesChild, fn, u"bar.txt")
1232 d.addCallback(lambda res:
1233 self.failUnlessChildContentsAre(fn, u"bar.txt",
1234 self.NEWFILE_CONTENTS))
1237 def test_POST_upload_no_replace_ok(self):
1238 d = self.POST(self.public_url + "/foo?replace=false", t="upload",
1239 file=("new.txt", self.NEWFILE_CONTENTS))
1240 d.addCallback(lambda res: self.GET(self.public_url + "/foo/new.txt"))
1241 d.addCallback(lambda res: self.failUnlessEqual(res,
1242 self.NEWFILE_CONTENTS))
1245 def test_POST_upload_no_replace_queryarg(self):
1246 d = self.POST(self.public_url + "/foo?replace=false", t="upload",
1247 file=("bar.txt", self.NEWFILE_CONTENTS))
1248 d.addBoth(self.shouldFail, error.Error,
1249 "POST_upload_no_replace_queryarg",
1251 "There was already a child by that name, and you asked me "
1252 "to not replace it")
1253 d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt"))
1254 d.addCallback(self.failUnlessIsBarDotTxt)
1257 def test_POST_upload_no_replace_field(self):
1258 d = self.POST(self.public_url + "/foo", t="upload", replace="false",
1259 file=("bar.txt", self.NEWFILE_CONTENTS))
1260 d.addBoth(self.shouldFail, error.Error, "POST_upload_no_replace_field",
1262 "There was already a child by that name, and you asked me "
1263 "to not replace it")
1264 d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt"))
1265 d.addCallback(self.failUnlessIsBarDotTxt)
1268 def test_POST_upload_whendone(self):
1269 d = self.POST(self.public_url + "/foo", t="upload", when_done="/THERE",
1270 file=("new.txt", self.NEWFILE_CONTENTS))
1271 d.addBoth(self.shouldRedirect, "/THERE")
1273 d.addCallback(lambda res:
1274 self.failUnlessChildContentsAre(fn, u"new.txt",
1275 self.NEWFILE_CONTENTS))
1278 def test_POST_upload_named(self):
1280 d = self.POST(self.public_url + "/foo", t="upload",
1281 name="new.txt", file=self.NEWFILE_CONTENTS)
1282 d.addCallback(self.failUnlessURIMatchesChild, fn, u"new.txt")
1283 d.addCallback(lambda res:
1284 self.failUnlessChildContentsAre(fn, u"new.txt",
1285 self.NEWFILE_CONTENTS))
1288 def test_POST_upload_named_badfilename(self):
1289 d = self.POST(self.public_url + "/foo", t="upload",
1290 name="slashes/are/bad.txt", file=self.NEWFILE_CONTENTS)
1291 d.addBoth(self.shouldFail, error.Error,
1292 "test_POST_upload_named_badfilename",
1294 "name= may not contain a slash",
1296 # make sure that nothing was added
1297 d.addCallback(lambda res:
1298 self.failUnlessNodeKeysAre(self._foo_node,
1299 [u"bar.txt", u"blockingfile",
1300 u"empty", u"n\u00fc.txt",
1304 def test_POST_mkdir(self): # return value?
1305 d = self.POST(self.public_url + "/foo", t="mkdir", name="newdir")
1306 d.addCallback(lambda res: self._foo_node.get(u"newdir"))
1307 d.addCallback(self.failUnlessNodeKeysAre, [])
1310 def test_POST_mkdir_no_parentdir_noredirect(self):
1311 d = self.POST("/uri?t=mkdir")
1312 def _after_mkdir(res):
1313 uri.NewDirectoryURI.init_from_string(res)
1314 d.addCallback(_after_mkdir)
1317 def test_POST_mkdir_no_parentdir_redirect(self):
1318 d = self.POST("/uri?t=mkdir&redirect_to_result=true")
1319 d.addBoth(self.shouldRedirect, None, statuscode='303')
1320 def _check_target(target):
1321 target = urllib.unquote(target)
1322 self.failUnless(target.startswith("uri/URI:DIR2:"), target)
1323 d.addCallback(_check_target)
1326 def test_POST_noparent_bad(self):
1327 d = self.shouldHTTPError2("POST /uri?t=bogus", 400, "Bad Request",
1328 "/uri accepts only PUT, PUT?t=mkdir, "
1329 "POST?t=upload, and POST?t=mkdir",
1330 self.POST, "/uri?t=bogus")
1333 def test_welcome_page_mkdir_button(self):
1334 # Fetch the welcome page.
1336 def _after_get_welcome_page(res):
1337 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)
1338 mo = MKDIR_BUTTON_RE.search(res)
1339 formaction = mo.group(1)
1341 formaname = mo.group(3)
1342 formavalue = mo.group(4)
1343 return (formaction, formt, formaname, formavalue)
1344 d.addCallback(_after_get_welcome_page)
1345 def _after_parse_form(res):
1346 (formaction, formt, formaname, formavalue) = res
1347 return self.POST("/%s?t=%s&%s=%s" % (formaction, formt, formaname, formavalue))
1348 d.addCallback(_after_parse_form)
1349 d.addBoth(self.shouldRedirect, None, statuscode='303')
1352 def test_POST_mkdir_replace(self): # return value?
1353 d = self.POST(self.public_url + "/foo", t="mkdir", name="sub")
1354 d.addCallback(lambda res: self._foo_node.get(u"sub"))
1355 d.addCallback(self.failUnlessNodeKeysAre, [])
1358 def test_POST_mkdir_no_replace_queryarg(self): # return value?
1359 d = self.POST(self.public_url + "/foo?replace=false", t="mkdir", name="sub")
1360 d.addBoth(self.shouldFail, error.Error,
1361 "POST_mkdir_no_replace_queryarg",
1363 "There was already a child by that name, and you asked me "
1364 "to not replace it")
1365 d.addCallback(lambda res: self._foo_node.get(u"sub"))
1366 d.addCallback(self.failUnlessNodeKeysAre, [u"baz.txt"])
1369 def test_POST_mkdir_no_replace_field(self): # return value?
1370 d = self.POST(self.public_url + "/foo", t="mkdir", name="sub",
1372 d.addBoth(self.shouldFail, error.Error, "POST_mkdir_no_replace_field",
1374 "There was already a child by that name, and you asked me "
1375 "to not replace it")
1376 d.addCallback(lambda res: self._foo_node.get(u"sub"))
1377 d.addCallback(self.failUnlessNodeKeysAre, [u"baz.txt"])
1380 def test_POST_mkdir_whendone_field(self):
1381 d = self.POST(self.public_url + "/foo",
1382 t="mkdir", name="newdir", when_done="/THERE")
1383 d.addBoth(self.shouldRedirect, "/THERE")
1384 d.addCallback(lambda res: self._foo_node.get(u"newdir"))
1385 d.addCallback(self.failUnlessNodeKeysAre, [])
1388 def test_POST_mkdir_whendone_queryarg(self):
1389 d = self.POST(self.public_url + "/foo?when_done=/THERE",
1390 t="mkdir", name="newdir")
1391 d.addBoth(self.shouldRedirect, "/THERE")
1392 d.addCallback(lambda res: self._foo_node.get(u"newdir"))
1393 d.addCallback(self.failUnlessNodeKeysAre, [])
1396 def test_POST_bad_t(self):
1397 d = self.shouldFail2(error.Error, "POST_bad_t", "400 Bad Request",
1399 self.POST, self.public_url + "/foo", t="BOGUS")
1402 def test_POST_set_children(self):
1403 contents9, n9, newuri9 = self.makefile(9)
1404 contents10, n10, newuri10 = self.makefile(10)
1405 contents11, n11, newuri11 = self.makefile(11)
1408 "atomic_added_1": [ "filenode", { "rw_uri": "%s",
1411 "ctime": 1002777696.7564139,
1412 "mtime": 1002777696.7564139
1415 "atomic_added_2": [ "filenode", { "rw_uri": "%s",
1418 "ctime": 1002777696.7564139,
1419 "mtime": 1002777696.7564139
1422 "atomic_added_3": [ "filenode", { "rw_uri": "%s",
1425 "ctime": 1002777696.7564139,
1426 "mtime": 1002777696.7564139
1429 }""" % (newuri9, newuri10, newuri11)
1431 url = self.webish_url + self.public_url + "/foo" + "?t=set_children"
1433 d = client.getPage(url, method="POST", postdata=reqbody)
1435 self.failUnlessURIMatchesChild(newuri9, self._foo_node, u"atomic_added_1")
1436 self.failUnlessURIMatchesChild(newuri10, self._foo_node, u"atomic_added_2")
1437 self.failUnlessURIMatchesChild(newuri11, self._foo_node, u"atomic_added_3")
1439 d.addCallback(_then)
1442 def test_POST_put_uri(self):
1443 contents, n, newuri = self.makefile(8)
1444 d = self.POST(self.public_url + "/foo", t="uri", name="new.txt", uri=newuri)
1445 d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"new.txt")
1446 d.addCallback(lambda res:
1447 self.failUnlessChildContentsAre(self._foo_node, u"new.txt",
1451 def test_POST_put_uri_replace(self):
1452 contents, n, newuri = self.makefile(8)
1453 d = self.POST(self.public_url + "/foo", t="uri", name="bar.txt", uri=newuri)
1454 d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"bar.txt")
1455 d.addCallback(lambda res:
1456 self.failUnlessChildContentsAre(self._foo_node, u"bar.txt",
1460 def test_POST_put_uri_no_replace_queryarg(self):
1461 contents, n, newuri = self.makefile(8)
1462 d = self.POST(self.public_url + "/foo?replace=false", t="uri",
1463 name="bar.txt", uri=newuri)
1464 d.addBoth(self.shouldFail, error.Error,
1465 "POST_put_uri_no_replace_queryarg",
1467 "There was already a child by that name, and you asked me "
1468 "to not replace it")
1469 d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt"))
1470 d.addCallback(self.failUnlessIsBarDotTxt)
1473 def test_POST_put_uri_no_replace_field(self):
1474 contents, n, newuri = self.makefile(8)
1475 d = self.POST(self.public_url + "/foo", t="uri", replace="false",
1476 name="bar.txt", uri=newuri)
1477 d.addBoth(self.shouldFail, error.Error,
1478 "POST_put_uri_no_replace_field",
1480 "There was already a child by that name, and you asked me "
1481 "to not replace it")
1482 d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt"))
1483 d.addCallback(self.failUnlessIsBarDotTxt)
1486 def test_POST_delete(self):
1487 d = self.POST(self.public_url + "/foo", t="delete", name="bar.txt")
1488 d.addCallback(lambda res: self._foo_node.list())
1489 def _check(children):
1490 self.failIf(u"bar.txt" in children)
1491 d.addCallback(_check)
1494 def test_POST_rename_file(self):
1495 d = self.POST(self.public_url + "/foo", t="rename",
1496 from_name="bar.txt", to_name='wibble.txt')
1497 d.addCallback(lambda res:
1498 self.failIfNodeHasChild(self._foo_node, u"bar.txt"))
1499 d.addCallback(lambda res:
1500 self.failUnlessNodeHasChild(self._foo_node, u"wibble.txt"))
1501 d.addCallback(lambda res: self.GET(self.public_url + "/foo/wibble.txt"))
1502 d.addCallback(self.failUnlessIsBarDotTxt)
1503 d.addCallback(lambda res: self.GET(self.public_url + "/foo/wibble.txt?t=json"))
1504 d.addCallback(self.failUnlessIsBarJSON)
1507 def test_POST_rename_file_replace(self):
1508 # rename a file and replace a directory with it
1509 d = self.POST(self.public_url + "/foo", t="rename",
1510 from_name="bar.txt", to_name='empty')
1511 d.addCallback(lambda res:
1512 self.failIfNodeHasChild(self._foo_node, u"bar.txt"))
1513 d.addCallback(lambda res:
1514 self.failUnlessNodeHasChild(self._foo_node, u"empty"))
1515 d.addCallback(lambda res: self.GET(self.public_url + "/foo/empty"))
1516 d.addCallback(self.failUnlessIsBarDotTxt)
1517 d.addCallback(lambda res: self.GET(self.public_url + "/foo/empty?t=json"))
1518 d.addCallback(self.failUnlessIsBarJSON)
1521 def test_POST_rename_file_no_replace_queryarg(self):
1522 # rename a file and replace a directory with it
1523 d = self.POST(self.public_url + "/foo?replace=false", t="rename",
1524 from_name="bar.txt", to_name='empty')
1525 d.addBoth(self.shouldFail, error.Error,
1526 "POST_rename_file_no_replace_queryarg",
1528 "There was already a child by that name, and you asked me "
1529 "to not replace it")
1530 d.addCallback(lambda res: self.GET(self.public_url + "/foo/empty?t=json"))
1531 d.addCallback(self.failUnlessIsEmptyJSON)
1534 def test_POST_rename_file_no_replace_field(self):
1535 # rename a file and replace a directory with it
1536 d = self.POST(self.public_url + "/foo", t="rename", replace="false",
1537 from_name="bar.txt", to_name='empty')
1538 d.addBoth(self.shouldFail, error.Error,
1539 "POST_rename_file_no_replace_field",
1541 "There was already a child by that name, and you asked me "
1542 "to not replace it")
1543 d.addCallback(lambda res: self.GET(self.public_url + "/foo/empty?t=json"))
1544 d.addCallback(self.failUnlessIsEmptyJSON)
1547 def failUnlessIsEmptyJSON(self, res):
1548 data = simplejson.loads(res)
1549 self.failUnlessEqual(data[0], "dirnode", data)
1550 self.failUnlessEqual(len(data[1]["children"]), 0)
1552 def test_POST_rename_file_slash_fail(self):
1553 d = self.POST(self.public_url + "/foo", t="rename",
1554 from_name="bar.txt", to_name='kirk/spock.txt')
1555 d.addBoth(self.shouldFail, error.Error,
1556 "test_POST_rename_file_slash_fail",
1558 "to_name= may not contain a slash",
1560 d.addCallback(lambda res:
1561 self.failUnlessNodeHasChild(self._foo_node, u"bar.txt"))
1562 d.addCallback(lambda res: self.POST(self.public_url, t="rename",
1563 from_name="foo/bar.txt", to_name='george.txt'))
1564 d.addBoth(self.shouldFail, error.Error,
1565 "test_POST_rename_file_slash_fail",
1567 "from_name= may not contain a slash",
1569 d.addCallback(lambda res:
1570 self.failUnlessNodeHasChild(self.public_root, u"foo"))
1571 d.addCallback(lambda res:
1572 self.failIfNodeHasChild(self.public_root, u"george.txt"))
1573 d.addCallback(lambda res:
1574 self.failUnlessNodeHasChild(self._foo_node, u"bar.txt"))
1575 d.addCallback(lambda res: self.GET(self.public_url + "/foo?t=json"))
1576 d.addCallback(self.failUnlessIsFooJSON)
1579 def test_POST_rename_dir(self):
1580 d = self.POST(self.public_url, t="rename",
1581 from_name="foo", to_name='plunk')
1582 d.addCallback(lambda res:
1583 self.failIfNodeHasChild(self.public_root, u"foo"))
1584 d.addCallback(lambda res:
1585 self.failUnlessNodeHasChild(self.public_root, u"plunk"))
1586 d.addCallback(lambda res: self.GET(self.public_url + "/plunk?t=json"))
1587 d.addCallback(self.failUnlessIsFooJSON)
1590 def shouldRedirect(self, res, target=None, statuscode=None):
1591 """ If target is not None then the redirection has to go to target. If
1592 statuscode is not None then the redirection has to be accomplished with
1593 that HTTP status code."""
1594 if not isinstance(res, failure.Failure):
1595 self.fail("we were expecting to get redirected %s, not get an"
1596 " actual page: %s" % ((target is None) and "somewhere" or ("to " + target), res))
1597 res.trap(error.PageRedirect)
1598 if statuscode is not None:
1599 self.failUnlessEqual(res.value.status, statuscode)
1600 if target is not None:
1601 # the PageRedirect does not seem to capture the uri= query arg
1602 # properly, so we can't check for it.
1603 realtarget = self.webish_url + target
1604 self.failUnlessEqual(res.value.location, realtarget)
1605 return res.value.location
1607 def test_GET_URI_form(self):
1608 base = "/uri?uri=%s" % self._bar_txt_uri
1609 # this is supposed to give us a redirect to /uri/$URI, plus arguments
1610 targetbase = "/uri/%s" % urllib.quote(self._bar_txt_uri)
1612 d.addBoth(self.shouldRedirect, targetbase)
1613 d.addCallback(lambda res: self.GET(base+"&filename=bar.txt"))
1614 d.addBoth(self.shouldRedirect, targetbase+"?filename=bar.txt")
1615 d.addCallback(lambda res: self.GET(base+"&t=json"))
1616 d.addBoth(self.shouldRedirect, targetbase+"?t=json")
1617 d.addCallback(self.log, "about to get file by uri")
1618 d.addCallback(lambda res: self.GET(base, followRedirect=True))
1619 d.addCallback(self.failUnlessIsBarDotTxt)
1620 d.addCallback(self.log, "got file by uri, about to get dir by uri")
1621 d.addCallback(lambda res: self.GET("/uri?uri=%s&t=json" % self._foo_uri,
1622 followRedirect=True))
1623 d.addCallback(self.failUnlessIsFooJSON)
1624 d.addCallback(self.log, "got dir by uri")
1628 def test_GET_rename_form(self):
1629 d = self.GET(self.public_url + "/foo?t=rename-form&name=bar.txt",
1630 followRedirect=True) # XXX [ ] todo: figure out why '.../foo' doesn't work
1632 self.failUnless(re.search(r'name="when_done" value=".*%s/foo/' % (urllib.quote(self.public_url),), res), (r'name="when_done" value=".*%s/foo/' % (urllib.quote(self.public_url),), res,))
1633 self.failUnless(re.search(r'name="from_name" value="bar\.txt"', res))
1634 d.addCallback(_check)
1637 def log(self, res, msg):
1638 #print "MSG: %s RES: %s" % (msg, res)
1642 def test_GET_URI_URL(self):
1643 base = "/uri/%s" % self._bar_txt_uri
1645 d.addCallback(self.failUnlessIsBarDotTxt)
1646 d.addCallback(lambda res: self.GET(base+"?filename=bar.txt"))
1647 d.addCallback(self.failUnlessIsBarDotTxt)
1648 d.addCallback(lambda res: self.GET(base+"?filename=bar.txt&save=true"))
1649 d.addCallback(self.failUnlessIsBarDotTxt)
1652 def test_GET_URI_URL_dir(self):
1653 base = "/uri/%s?t=json" % self._foo_uri
1655 d.addCallback(self.failUnlessIsFooJSON)
1658 def test_GET_URI_URL_missing(self):
1659 base = "/uri/%s" % self._bad_file_uri
1661 d.addBoth(self.shouldHTTPError, "test_GET_URI_URL_missing",
1662 http.GONE, response_substring="NotEnoughSharesError")
1663 # TODO: how can we exercise both sides of WebDownloadTarget.fail
1664 # here? we must arrange for a download to fail after target.open()
1665 # has been called, and then inspect the response to see that it is
1666 # shorter than we expected.
1669 def test_PUT_NEWFILEURL_uri(self):
1670 contents, n, new_uri = self.makefile(8)
1671 d = self.PUT(self.public_url + "/foo/new.txt?t=uri", new_uri)
1672 d.addCallback(lambda res: self.failUnlessEqual(res.strip(), new_uri))
1673 d.addCallback(lambda res:
1674 self.failUnlessChildContentsAre(self._foo_node, u"new.txt",
1678 def test_PUT_NEWFILEURL_uri_replace(self):
1679 contents, n, new_uri = self.makefile(8)
1680 d = self.PUT(self.public_url + "/foo/bar.txt?t=uri", new_uri)
1681 d.addCallback(lambda res: self.failUnlessEqual(res.strip(), new_uri))
1682 d.addCallback(lambda res:
1683 self.failUnlessChildContentsAre(self._foo_node, u"bar.txt",
1687 def test_PUT_NEWFILEURL_uri_no_replace(self):
1688 contents, n, new_uri = self.makefile(8)
1689 d = self.PUT(self.public_url + "/foo/bar.txt?t=uri&replace=false", new_uri)
1690 d.addBoth(self.shouldFail, error.Error, "PUT_NEWFILEURL_uri_no_replace",
1692 "There was already a child by that name, and you asked me "
1693 "to not replace it")
1696 def test_PUT_NEWFILE_URI(self):
1697 file_contents = "New file contents here\n"
1698 d = self.PUT("/uri", file_contents)
1700 self.failUnless(uri in FakeCHKFileNode.all_contents)
1701 self.failUnlessEqual(FakeCHKFileNode.all_contents[uri],
1703 return self.GET("/uri/%s" % uri)
1704 d.addCallback(_check)
1706 self.failUnlessEqual(res, file_contents)
1707 d.addCallback(_check2)
1710 def test_PUT_NEWFILE_URI_only_PUT(self):
1711 d = self.PUT("/uri?t=bogus", "")
1712 d.addBoth(self.shouldFail, error.Error,
1713 "PUT_NEWFILE_URI_only_PUT",
1715 "/uri only accepts PUT and PUT?t=mkdir")
1718 def test_PUT_NEWFILE_URI_mutable(self):
1719 file_contents = "New file contents here\n"
1720 d = self.PUT("/uri?mutable=true", file_contents)
1721 def _check_mutable(uri):
1724 self.failUnless(IMutableFileURI.providedBy(u))
1725 self.failUnless(u.storage_index in FakeMutableFileNode.all_contents)
1726 n = self.s.create_node_from_uri(uri)
1727 return n.download_to_data()
1728 d.addCallback(_check_mutable)
1729 def _check2_mutable(data):
1730 self.failUnlessEqual(data, file_contents)
1731 d.addCallback(_check2_mutable)
1735 self.failUnless(uri in FakeCHKFileNode.all_contents)
1736 self.failUnlessEqual(FakeCHKFileNode.all_contents[uri],
1738 return self.GET("/uri/%s" % uri)
1739 d.addCallback(_check)
1741 self.failUnlessEqual(res, file_contents)
1742 d.addCallback(_check2)
1745 def test_PUT_mkdir(self):
1746 d = self.PUT("/uri?t=mkdir", "")
1748 n = self.s.create_node_from_uri(uri.strip())
1749 d2 = self.failUnlessNodeKeysAre(n, [])
1750 d2.addCallback(lambda res:
1751 self.GET("/uri/%s?t=json" % uri))
1753 d.addCallback(_check)
1754 d.addCallback(self.failUnlessIsEmptyJSON)
1757 def test_POST_check(self):
1758 d = self.POST(self.public_url + "/foo", t="check", name="bar.txt")
1760 # this returns a string form of the results, which are probably
1761 # None since we're using fake filenodes.
1762 # TODO: verify that the check actually happened, by changing
1763 # FakeCHKFileNode to count how many times .check() has been
1766 d.addCallback(_done)
1769 def test_bad_method(self):
1770 url = self.webish_url + self.public_url + "/foo/bar.txt"
1771 d = self.shouldHTTPError2("test_bad_method", 404, "Not Found", None,
1772 client.getPage, url, method="BOGUS")
1775 def test_short_url(self):
1776 url = self.webish_url + "/uri"
1777 d = self.shouldHTTPError2("test_short_url", 404, "Not Found", None,
1778 client.getPage, url, method="DELETE")