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
9 from allmydata.util import fileutil
10 from allmydata.test.common import NonGridDirectoryNode, FakeCHKFileNode, FakeMutableFileNode, create_chk_filenode
11 from allmydata.interfaces import IURI, INewDirectoryURI, IReadonlyNewDirectoryURI, IFileURI, IMutableFileURI, IMutableFileNode
13 # create a fake uploader/downloader, and a couple of fake dirnodes, then
14 # create a webserver that works against them
16 class FakeIntroducerClient:
17 def get_all_connectors(self):
19 def get_all_connections_for(self, service_name):
21 def get_all_peerids(self):
24 class FakeClient(service.MultiService):
25 nodeid = "fake_nodeid"
26 basedir = "fake_basedir"
27 def get_versions(self):
28 return {'allmydata': "fake",
33 introducer_furl = "None"
34 introducer_client = FakeIntroducerClient()
35 def connected_to_introducer(self):
38 def create_node_from_uri(self, uri):
40 if (INewDirectoryURI.providedBy(u)
41 or IReadonlyNewDirectoryURI.providedBy(u)):
42 return NonGridDirectoryNode(self).init_from_uri(u)
43 if IFileURI.providedBy(u):
44 return FakeCHKFileNode(u, self)
45 assert IMutableFileURI.providedBy(u), u
46 return FakeMutableFileNode(self).init_from_uri(u)
48 def create_empty_dirnode(self):
49 n = NonGridDirectoryNode(self)
51 d.addCallback(lambda res: n)
54 def create_mutable_file(self, contents=""):
55 n = FakeMutableFileNode(self)
56 return n.create(contents)
58 def upload(self, uploadable):
59 d = uploadable.get_size()
60 d.addCallback(lambda size: uploadable.read(size))
63 n = create_chk_filenode(self, data)
64 results = upload.UploadResults()
65 results.uri = n.get_uri()
67 d.addCallback(_got_data)
71 class WebMixin(object):
75 self.ws = s = webish.WebishServer("0")
76 s.allow_local_access(True)
77 s.setServiceParent(self.s)
78 port = s.listener._port.getHost().port
79 self.webish_url = "http://localhost:%d" % port
81 l = [ self.s.create_empty_dirnode() for x in range(6) ]
82 d = defer.DeferredList(l)
84 self.public_root = res[0][1]
85 assert interfaces.IDirectoryNode.providedBy(self.public_root), res
86 self.public_url = "/uri/" + self.public_root.get_uri()
87 self.private_root = res[1][1]
91 self._foo_uri = foo.get_uri()
92 self._foo_readonly_uri = foo.get_readonly_uri()
93 # NOTE: we ignore the deferred on all set_uri() calls, because we
94 # know the fake nodes do these synchronously
95 self.public_root.set_uri("foo", foo.get_uri())
97 self.BAR_CONTENTS, n, self._bar_txt_uri = self.makefile(0)
98 foo.set_uri("bar.txt", self._bar_txt_uri)
99 foo.set_uri("empty", res[3][1].get_uri())
100 sub_uri = res[4][1].get_uri()
101 foo.set_uri("sub", sub_uri)
102 sub = self.s.create_node_from_uri(sub_uri)
104 _ign, n, blocking_uri = self.makefile(1)
105 foo.set_uri("blockingfile", blocking_uri)
107 _ign, n, baz_file = self.makefile(2)
108 sub.set_uri("baz.txt", baz_file)
110 _ign, n, self._bad_file_uri = self.makefile(3)
111 # this uri should not be downloadable
112 del FakeCHKFileNode.all_contents[self._bad_file_uri]
115 self.public_root.set_uri("reedownlee", rodir.get_readonly_uri())
116 rodir.set_uri("nor", baz_file)
121 # public/foo/blockingfile
124 # public/foo/sub/baz.txt
126 # public/reedownlee/nor
127 self.NEWFILE_CONTENTS = "newfile contents\n"
131 def makefile(self, number):
132 contents = "contents of file %s\n" % number
133 n = create_chk_filenode(self.s, contents)
134 return contents, n, n.get_uri()
137 return self.s.stopService()
139 def failUnlessIsBarDotTxt(self, res):
140 self.failUnlessEqual(res, self.BAR_CONTENTS)
142 def failUnlessIsBarJSON(self, res):
143 data = simplejson.loads(res)
144 self.failUnless(isinstance(data, list))
145 self.failUnlessEqual(data[0], "filenode")
146 self.failUnless(isinstance(data[1], dict))
147 self.failIf("rw_uri" in data[1]) # immutable
148 self.failUnlessEqual(data[1]["ro_uri"], self._bar_txt_uri)
149 self.failUnlessEqual(data[1]["size"], len(self.BAR_CONTENTS))
151 def failUnlessIsFooJSON(self, res):
152 data = simplejson.loads(res)
153 self.failUnless(isinstance(data, list))
154 self.failUnlessEqual(data[0], "dirnode", res)
155 self.failUnless(isinstance(data[1], dict))
156 self.failUnless("rw_uri" in data[1]) # mutable
157 self.failUnlessEqual(data[1]["rw_uri"], self._foo_uri)
158 self.failUnlessEqual(data[1]["ro_uri"], self._foo_readonly_uri)
160 kidnames = sorted(data[1]["children"])
161 self.failUnlessEqual(kidnames,
162 ["bar.txt", "blockingfile", "empty", "sub"])
163 kids = data[1]["children"]
164 self.failUnlessEqual(kids["sub"][0], "dirnode")
165 self.failUnlessEqual(kids["bar.txt"][0], "filenode")
166 self.failUnlessEqual(kids["bar.txt"][1]["size"], len(self.BAR_CONTENTS))
167 self.failUnlessEqual(kids["bar.txt"][1]["ro_uri"], self._bar_txt_uri)
169 def GET(self, urlpath, followRedirect=False):
170 url = self.webish_url + urlpath
171 return client.getPage(url, method="GET", followRedirect=followRedirect)
173 def PUT(self, urlpath, data):
174 url = self.webish_url + urlpath
175 return client.getPage(url, method="PUT", postdata=data)
177 def DELETE(self, urlpath):
178 url = self.webish_url + urlpath
179 return client.getPage(url, method="DELETE")
181 def POST(self, urlpath, followRedirect=False, **fields):
182 url = self.webish_url + urlpath
183 sepbase = "boogabooga"
187 form.append('Content-Disposition: form-data; name="_charset"')
191 for name, value in fields.iteritems():
192 if isinstance(value, tuple):
193 filename, value = value
194 form.append('Content-Disposition: form-data; name="%s"; '
195 'filename="%s"' % (name, filename))
197 form.append('Content-Disposition: form-data; name="%s"' % name)
199 form.append(str(value))
202 body = "\r\n".join(form) + "\r\n"
203 headers = {"content-type": "multipart/form-data; boundary=%s" % sepbase,
205 return client.getPage(url, method="POST", postdata=body,
206 headers=headers, followRedirect=followRedirect)
208 def shouldFail(self, res, expected_failure, which,
209 substring=None, response_substring=None):
210 if isinstance(res, failure.Failure):
211 res.trap(expected_failure)
213 self.failUnless(substring in str(res),
214 "substring '%s' not in '%s'"
215 % (substring, str(res)))
216 if response_substring:
217 self.failUnless(response_substring in res.value.response,
218 "respose substring '%s' not in '%s'"
219 % (response_substring, res.value.response))
221 self.fail("%s was supposed to raise %s, not get '%s'" %
222 (which, expected_failure, res))
224 def shouldFail2(self, expected_failure, which, substring,
225 callable, *args, **kwargs):
226 assert substring is None or isinstance(substring, str)
227 d = defer.maybeDeferred(callable, *args, **kwargs)
229 if isinstance(res, failure.Failure):
230 res.trap(expected_failure)
232 self.failUnless(substring in str(res),
233 "substring '%s' not in '%s'"
234 % (substring, str(res)))
236 self.fail("%s was supposed to raise %s, not get '%s'" %
237 (which, expected_failure, res))
241 def should404(self, res, which):
242 if isinstance(res, failure.Failure):
243 res.trap(error.Error)
244 self.failUnlessEqual(res.value.status, "404")
246 self.fail("%s was supposed to Error(404), not get '%s'" %
249 def shouldHTTPError(self, res, which, code=None, substring=None,
250 response_substring=None):
251 if isinstance(res, failure.Failure):
252 res.trap(error.Error)
254 self.failUnlessEqual(res.value.status, str(code))
256 self.failUnless(substring in str(res),
257 "substring '%s' not in '%s'"
258 % (substring, str(res)))
259 if response_substring:
260 self.failUnless(response_substring in res.value.response,
261 "respose substring '%s' not in '%s'"
262 % (response_substring, res.value.response))
264 self.fail("%s was supposed to Error(%s), not get '%s'" %
267 def shouldHTTPError2(self, which,
268 code=None, substring=None, response_substring=None,
269 callable=None, *args, **kwargs):
270 assert substring is None or isinstance(substring, str)
272 d = defer.maybeDeferred(callable, *args, **kwargs)
273 d.addBoth(self.shouldHTTPError, which,
274 code, substring, response_substring)
278 class Web(WebMixin, unittest.TestCase):
279 def test_create(self):
282 def test_welcome(self):
285 self.failUnless('Welcome To AllMyData' in res)
286 self.failUnless('Tahoe' in res)
288 self.s.basedir = 'web/test_welcome'
289 fileutil.make_dirs("web/test_welcome")
290 fileutil.make_dirs("web/test_welcome/private")
292 d.addCallback(_check)
295 def test_provisioning_math(self):
296 self.failUnlessEqual(provisioning.binomial(10, 0), 1)
297 self.failUnlessEqual(provisioning.binomial(10, 1), 10)
298 self.failUnlessEqual(provisioning.binomial(10, 2), 45)
299 self.failUnlessEqual(provisioning.binomial(10, 9), 10)
300 self.failUnlessEqual(provisioning.binomial(10, 10), 1)
302 def test_provisioning(self):
303 d = self.GET("/provisioning/")
305 self.failUnless('Tahoe Provisioning Tool' in res)
306 fields = {'filled': True,
307 "num_users": int(50e3),
308 "files_per_user": 1000,
309 "space_per_user": int(1e9),
310 "sharing_ratio": 1.0,
311 "encoding_parameters": "3-of-10-5",
313 "ownership_mode": "A",
314 "download_rate": 100,
319 return self.POST("/provisioning/", **fields)
321 d.addCallback(_check)
323 self.failUnless('Tahoe Provisioning Tool' in res)
324 self.failUnless("Share space consumed: 167.01TB" in res)
326 fields = {'filled': True,
327 "num_users": int(50e6),
328 "files_per_user": 1000,
329 "space_per_user": int(5e9),
330 "sharing_ratio": 1.0,
331 "encoding_parameters": "25-of-100-50",
332 "num_servers": 30000,
333 "ownership_mode": "E",
334 "drive_failure_model": "U",
336 "download_rate": 1000,
341 return self.POST("/provisioning/", **fields)
342 d.addCallback(_check2)
344 self.failUnless("Share space consumed: huge!" in res)
345 fields = {'filled': True}
346 return self.POST("/provisioning/", **fields)
347 d.addCallback(_check3)
349 self.failUnless("Share space consumed:" in res)
350 d.addCallback(_check4)
353 def test_GET_FILEURL(self):
354 d = self.GET(self.public_url + "/foo/bar.txt")
355 d.addCallback(self.failUnlessIsBarDotTxt)
358 def test_GET_FILEURL_save(self):
359 d = self.GET(self.public_url + "/foo/bar.txt?save=bar.txt")
360 # TODO: look at the headers, expect a Content-Disposition: attachment
362 d.addCallback(self.failUnlessIsBarDotTxt)
365 def test_GET_FILEURL_download(self):
366 d = self.GET(self.public_url + "/foo/bar.txt?t=download")
367 d.addCallback(self.failUnlessIsBarDotTxt)
370 def test_GET_FILEURL_missing(self):
371 d = self.GET(self.public_url + "/foo/missing")
372 d.addBoth(self.should404, "test_GET_FILEURL_missing")
375 def test_PUT_NEWFILEURL(self):
376 d = self.PUT(self.public_url + "/foo/new.txt", self.NEWFILE_CONTENTS)
377 # TODO: we lose the response code, so we can't check this
378 #self.failUnlessEqual(responsecode, 201)
379 d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, "new.txt")
380 d.addCallback(lambda res:
381 self.failUnlessChildContentsAre(self._foo_node, "new.txt",
382 self.NEWFILE_CONTENTS))
385 def test_PUT_NEWFILEURL_replace(self):
386 d = self.PUT(self.public_url + "/foo/bar.txt", self.NEWFILE_CONTENTS)
387 # TODO: we lose the response code, so we can't check this
388 #self.failUnlessEqual(responsecode, 200)
389 d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, "bar.txt")
390 d.addCallback(lambda res:
391 self.failUnlessChildContentsAre(self._foo_node, "bar.txt",
392 self.NEWFILE_CONTENTS))
395 def test_PUT_NEWFILEURL_no_replace(self):
396 d = self.PUT(self.public_url + "/foo/bar.txt?replace=false",
397 self.NEWFILE_CONTENTS)
398 d.addBoth(self.shouldFail, error.Error, "PUT_NEWFILEURL_no_replace",
400 "There was already a child by that name, and you asked me "
404 def test_PUT_NEWFILEURL_mkdirs(self):
405 d = self.PUT(self.public_url + "/foo/newdir/new.txt", self.NEWFILE_CONTENTS)
407 d.addCallback(self.failUnlessURIMatchesChild, fn, "newdir/new.txt")
408 d.addCallback(lambda res: self.failIfNodeHasChild(fn, "new.txt"))
409 d.addCallback(lambda res: self.failUnlessNodeHasChild(fn, "newdir"))
410 d.addCallback(lambda res:
411 self.failUnlessChildContentsAre(fn, "newdir/new.txt",
412 self.NEWFILE_CONTENTS))
415 def test_PUT_NEWFILEURL_blocked(self):
416 d = self.PUT(self.public_url + "/foo/blockingfile/new.txt",
417 self.NEWFILE_CONTENTS)
418 d.addBoth(self.shouldFail, error.Error, "PUT_NEWFILEURL_blocked",
420 "cannot create directory because there is a file in the way")
423 def test_DELETE_FILEURL(self):
424 d = self.DELETE(self.public_url + "/foo/bar.txt")
425 d.addCallback(lambda res:
426 self.failIfNodeHasChild(self._foo_node, "bar.txt"))
429 def test_DELETE_FILEURL_missing(self):
430 d = self.DELETE(self.public_url + "/foo/missing")
431 d.addBoth(self.should404, "test_DELETE_FILEURL_missing")
434 def test_DELETE_FILEURL_missing2(self):
435 d = self.DELETE(self.public_url + "/missing/missing")
436 d.addBoth(self.should404, "test_DELETE_FILEURL_missing2")
439 def test_GET_FILEURL_json(self):
440 # twisted.web.http.parse_qs ignores any query args without an '=', so
441 # I can't do "GET /path?json", I have to do "GET /path/t=json"
442 # instead. This may make it tricky to emulate the S3 interface
444 d = self.GET(self.public_url + "/foo/bar.txt?t=json")
445 d.addCallback(self.failUnlessIsBarJSON)
448 def test_GET_FILEURL_json_missing(self):
449 d = self.GET(self.public_url + "/foo/missing?json")
450 d.addBoth(self.should404, "test_GET_FILEURL_json_missing")
453 def disable_local_access(self, res=None):
454 self.ws.allow_local_access(False)
457 def test_GET_FILEURL_localfile(self):
458 localfile = os.path.abspath("web/GET_FILEURL_local file")
459 url = (self.public_url + "/foo/bar.txt?t=download&localfile=%s" %
460 urllib.quote(localfile))
461 fileutil.make_dirs("web")
464 self.failUnless(os.path.exists(localfile))
465 data = open(localfile, "rb").read()
466 self.failUnlessEqual(data, self.BAR_CONTENTS)
470 def test_GET_FILEURL_localfile_disabled(self):
471 localfile = os.path.abspath("web/GET_FILEURL_local file_disabled")
472 url = (self.public_url + "/foo/bar.txt?t=download&localfile=%s" %
473 urllib.quote(localfile))
474 fileutil.make_dirs("web")
475 self.disable_local_access()
477 d.addBoth(self.shouldFail, error.Error, "localfile disabled",
479 "local file access is disabled")
482 def test_GET_FILEURL_localfile_nonlocal(self):
483 # TODO: somehow pretend that we aren't local, and verify that the
484 # server refuses to write to local files, probably by changing the
485 # server's idea of what counts as "local".
486 old_LOCALHOST = webish.LOCALHOST
487 webish.LOCALHOST = "127.0.0.2"
488 localfile = os.path.abspath("web/GET_FILEURL_local file_nonlocal")
489 fileutil.make_dirs("web")
490 d = self.GET(self.public_url + "/foo/bar.txt?t=download&localfile=%s"
491 % urllib.quote(localfile))
492 d.addBoth(self.shouldFail, error.Error, "localfile non-local",
494 "localfile= or localdir= requires a local connection")
496 self.failIf(os.path.exists(localfile))
497 d.addCallback(_check)
499 webish.LOCALHOST = old_LOCALHOST
504 def test_GET_FILEURL_localfile_nonabsolute(self):
505 localfile = "web/nonabsolute/path"
506 fileutil.make_dirs("web/nonabsolute")
507 d = self.GET(self.public_url + "/foo/bar.txt?t=download&localfile=%s"
508 % urllib.quote(localfile))
509 d.addBoth(self.shouldFail, error.Error, "localfile non-absolute",
511 "localfile= or localdir= requires an absolute path")
513 self.failIf(os.path.exists(localfile))
514 d.addCallback(_check)
517 def test_PUT_NEWFILEURL_localfile(self):
518 localfile = os.path.abspath("web/PUT_NEWFILEURL_local file")
519 url = (self.public_url + "/foo/new.txt?t=upload&localfile=%s" %
520 urllib.quote(localfile))
521 fileutil.make_dirs("web")
522 f = open(localfile, "wb")
523 f.write(self.NEWFILE_CONTENTS)
525 d = self.PUT(url, "")
526 d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, "new.txt")
527 d.addCallback(lambda res:
528 self.failUnlessChildContentsAre(self._foo_node, "new.txt",
529 self.NEWFILE_CONTENTS))
532 def test_PUT_NEWFILEURL_localfile_missingarg(self):
533 url = self.public_url + "/foo/new.txt?t=upload"
534 d = self.shouldHTTPError2("test_PUT_NEWFILEURL_localfile_missing",
536 "t=upload requires localfile= or localdir=",
540 def test_PUT_NEWFILEURL_localfile_disabled(self):
541 localfile = os.path.abspath("web/PUT_NEWFILEURL_local file_disabled")
542 url = (self.public_url + "/foo/new.txt?t=upload&localfile=%s" %
543 urllib.quote(localfile))
544 fileutil.make_dirs("web")
545 f = open(localfile, "wb")
546 f.write(self.NEWFILE_CONTENTS)
548 self.disable_local_access()
549 d = self.PUT(url, "")
550 d.addBoth(self.shouldFail, error.Error, "put localfile disabled",
552 "local file access is disabled")
555 def test_PUT_NEWFILEURL_localfile_mkdirs(self):
556 localfile = os.path.abspath("web/PUT_NEWFILEURL_local file_mkdirs")
557 fileutil.make_dirs("web")
558 f = open(localfile, "wb")
559 f.write(self.NEWFILE_CONTENTS)
561 d = self.PUT(self.public_url + "/foo/newdir/new.txt?t=upload&localfile=%s"
562 % urllib.quote(localfile), "")
563 d.addCallback(self.failUnlessURIMatchesChild,
564 self._foo_node, "newdir/new.txt")
565 d.addCallback(lambda res:
566 self.failIfNodeHasChild(self._foo_node, "new.txt"))
567 d.addCallback(lambda res:
568 self.failUnlessNodeHasChild(self._foo_node, "newdir"))
569 d.addCallback(lambda res:
570 self.failUnlessChildContentsAre(self._foo_node,
572 self.NEWFILE_CONTENTS))
575 def test_GET_FILEURL_uri(self):
576 d = self.GET(self.public_url + "/foo/bar.txt?t=uri")
578 self.failUnlessEqual(res, self._bar_txt_uri)
579 d.addCallback(_check)
580 d.addCallback(lambda res:
581 self.GET(self.public_url + "/foo/bar.txt?t=readonly-uri"))
583 # for now, for files, uris and readonly-uris are the same
584 self.failUnlessEqual(res, self._bar_txt_uri)
585 d.addCallback(_check2)
588 def test_GET_FILEURL_badtype(self):
589 d = self.shouldHTTPError2("GET t=bogus", 400, "Bad Request",
592 self.public_url + "/foo/bar.txt?t=bogus")
595 def test_GET_FILEURL_uri_missing(self):
596 d = self.GET(self.public_url + "/foo/missing?t=uri")
597 d.addBoth(self.should404, "test_GET_FILEURL_uri_missing")
600 def test_GET_DIRURL(self):
601 # the addSlash means we get a redirect here
602 d = self.GET(self.public_url + "/foo", followRedirect=True)
604 # the FILE reference points to a URI, but it should end in bar.txt
605 self.failUnless(re.search(r'<td>'
606 '<a href="[^"]+bar.txt">bar.txt</a>'
609 '\s+<td>%d</td>' % len(self.BAR_CONTENTS)
611 # the DIR reference just points to a URI
612 self.failUnless(re.search(r'<td><a href="/uri/URI%3ADIR2%3A[^"]+">sub</a></td>'
613 '\s+<td>DIR</td>', res))
614 d.addCallback(_check)
616 # look at a directory which is readonly
617 d.addCallback(lambda res:
618 self.GET(self.public_url + "/reedownlee", followRedirect=True))
620 self.failUnless("(readonly)" in res, res)
621 self.failIf("Upload a file" in res, res)
622 d.addCallback(_check2)
624 # and at a directory that contains a readonly directory
625 d.addCallback(lambda res:
626 self.GET(self.public_url, followRedirect=True))
628 self.failUnless(re.search(r'<td><a href="/uri/URI%3ADIR2-RO%3A[^"]+">reedownlee</a>'
629 '</td>\s+<td>DIR-RO</td>', res))
630 d.addCallback(_check3)
634 def test_GET_DIRURL_badtype(self):
635 d = self.shouldHTTPError2("test_GET_DIRURL_badtype",
639 self.public_url + "/foo?t=bogus")
642 def test_GET_DIRURL_large(self):
643 # Nevow has a problem showing more than about 192 children of a
644 # directory: it uses defer.success() and d.addCallback in a way that
645 # can make the stack grow very quickly. See ticket #237 for details.
646 # To work around this, I think we'll need to put a 'return
647 # defer.fireEventually' in our render_ method. This test is intended
648 # to trigger the bug (and eventually verify that our workaround
649 # actually works), but it isn't yet failing for me.
651 d = self.s.create_empty_dirnode()
653 def _created(dirnode):
654 entries = [ (str(i), self._foo_uri) for i in range(COUNT) ]
655 d2 = dirnode.set_uris(entries)
656 d2.addCallback(lambda res: dirnode)
658 d.addCallback(_created)
661 large_url = "/uri/" + dirnode.get_uri() + "/"
662 return self.GET(large_url)
663 d.addCallback(_check)
666 m = r'<a href="/uri/URI%3ADIR2%3A[^"]+">' + ("%d" % (COUNT-1)) + r'</a>'
667 self.failUnless(re.search(m, res))
668 self.failIf("maximum recursion depth exceeded" in res)
671 test_GET_DIRURL_large.timeout= 240 # this hits 120-sec timeout on overloaded vm buildslaves
673 def test_GET_DIRURL_json(self):
674 d = self.GET(self.public_url + "/foo?t=json")
675 d.addCallback(self.failUnlessIsFooJSON)
678 def test_GET_DIRURL_manifest(self):
679 d = self.GET(self.public_url + "/foo?t=manifest", followRedirect=True)
681 self.failUnless("Refresh Capabilities" in manifest)
685 def test_GET_DIRURL_uri(self):
686 d = self.GET(self.public_url + "/foo?t=uri")
688 self.failUnlessEqual(res, self._foo_uri)
689 d.addCallback(_check)
692 def test_GET_DIRURL_readonly_uri(self):
693 d = self.GET(self.public_url + "/foo?t=readonly-uri")
695 self.failUnlessEqual(res, self._foo_readonly_uri)
696 d.addCallback(_check)
699 def test_PUT_NEWDIRURL(self):
700 d = self.PUT(self.public_url + "/foo/newdir?t=mkdir", "")
701 d.addCallback(lambda res:
702 self.failUnlessNodeHasChild(self._foo_node, "newdir"))
703 d.addCallback(lambda res: self._foo_node.get("newdir"))
704 d.addCallback(self.failUnlessNodeKeysAre, [])
707 def test_PUT_NEWDIRURL_replace(self):
708 d = self.PUT(self.public_url + "/foo/sub?t=mkdir", "")
709 d.addCallback(lambda res:
710 self.failUnlessNodeHasChild(self._foo_node, "sub"))
711 d.addCallback(lambda res: self._foo_node.get("sub"))
712 d.addCallback(self.failUnlessNodeKeysAre, [])
715 def test_PUT_NEWDIRURL_no_replace(self):
716 d = self.PUT(self.public_url + "/foo/sub?t=mkdir&replace=false", "")
717 d.addBoth(self.shouldFail, error.Error, "PUT_NEWDIRURL_no_replace",
719 "There was already a child by that name, and you asked me "
721 d.addCallback(lambda res:
722 self.failUnlessNodeHasChild(self._foo_node, "sub"))
723 d.addCallback(lambda res: self._foo_node.get("sub"))
724 d.addCallback(self.failUnlessNodeKeysAre, ["baz.txt"])
727 def test_PUT_NEWDIRURL_mkdirs(self):
728 d = self.PUT(self.public_url + "/foo/subdir/newdir?t=mkdir", "")
729 d.addCallback(lambda res:
730 self.failIfNodeHasChild(self._foo_node, "newdir"))
731 d.addCallback(lambda res:
732 self.failUnlessNodeHasChild(self._foo_node, "subdir"))
733 d.addCallback(lambda res:
734 self._foo_node.get_child_at_path("subdir/newdir"))
735 d.addCallback(self.failUnlessNodeKeysAre, [])
738 def test_DELETE_DIRURL(self):
739 d = self.DELETE(self.public_url + "/foo")
740 d.addCallback(lambda res:
741 self.failIfNodeHasChild(self.public_root, "foo"))
744 def test_DELETE_DIRURL_missing(self):
745 d = self.DELETE(self.public_url + "/foo/missing")
746 d.addBoth(self.should404, "test_DELETE_DIRURL_missing")
747 d.addCallback(lambda res:
748 self.failUnlessNodeHasChild(self.public_root, "foo"))
751 def test_DELETE_DIRURL_missing2(self):
752 d = self.DELETE(self.public_url + "/missing")
753 d.addBoth(self.should404, "test_DELETE_DIRURL_missing2")
756 def test_walker(self):
758 def _visitor(path, node, metadata):
759 out.append((path, node))
760 return defer.succeed(None)
761 w = webish.DirnodeWalkerMixin()
762 d = w.walk(self.public_root, _visitor)
764 names = [path for (path,node) in out]
765 self.failUnlessEqual(sorted(names),
768 ('foo','blockingfile'),
771 ('foo','sub','baz.txt'),
773 ('reedownlee', 'nor'),
775 subindex = names.index( ('foo', 'sub') )
776 bazindex = names.index( ('foo', 'sub', 'baz.txt') )
777 self.failUnless(subindex < bazindex)
778 for path,node in out:
779 if path[-1] in ('bar.txt', 'blockingfile', 'baz.txt', 'nor'):
780 self.failUnless(interfaces.IFileNode.providedBy(node))
782 self.failUnless(interfaces.IDirectoryNode.providedBy(node))
783 d.addCallback(_check)
786 def test_GET_DIRURL_localdir(self):
787 localdir = os.path.abspath("web/GET_DIRURL_local dir")
788 fileutil.make_dirs("web")
789 d = self.GET(self.public_url + "/foo?t=download&localdir=%s" %
790 urllib.quote(localdir))
792 barfile = os.path.join(localdir, "bar.txt")
793 self.failUnless(os.path.exists(barfile))
794 data = open(barfile, "rb").read()
795 self.failUnlessEqual(data, self.BAR_CONTENTS)
796 blockingfile = os.path.join(localdir, "blockingfile")
797 self.failUnless(os.path.exists(blockingfile))
798 subdir = os.path.join(localdir, "sub")
799 self.failUnless(os.path.isdir(subdir))
800 d.addCallback(_check)
803 def test_GET_DIRURL_localdir_disabled(self):
804 localdir = os.path.abspath("web/GET_DIRURL_local dir_disabled")
805 fileutil.make_dirs("web")
806 self.disable_local_access()
807 d = self.GET(self.public_url + "/foo?t=download&localdir=%s" %
808 urllib.quote(localdir))
809 d.addBoth(self.shouldFail, error.Error, "localfile disabled",
811 "local file access is disabled")
814 def test_GET_DIRURL_localdir_nonabsolute(self):
815 localdir = "web/nonabsolute/dir path"
816 fileutil.make_dirs("web/nonabsolute")
817 d = self.GET(self.public_url + "/foo?t=download&localdir=%s" %
818 urllib.quote(localdir))
819 d.addBoth(self.shouldFail, error.Error, "localdir non-absolute",
821 "localfile= or localdir= requires an absolute path")
823 self.failIf(os.path.exists(localdir))
824 d.addCallback(_check)
827 def test_GET_DIRURL_localdir_nolocaldir(self):
828 d = self.shouldHTTPError2("GET_DIRURL_localdir_nolocaldir",
830 "t=download requires localdir=",
832 self.public_url + "/foo?t=download")
835 def touch(self, localdir, filename):
836 path = os.path.join(localdir, filename)
838 f.write("contents of %s\n" % filename)
843 w = webish.DirnodeWalkerMixin()
844 def visitor(childpath, childnode, metadata):
846 d = w.walk(self.public_root, visitor)
849 def failUnlessNodeKeysAre(self, node, expected_keys):
851 def _check(children):
852 self.failUnlessEqual(sorted(children.keys()), sorted(expected_keys))
853 d.addCallback(_check)
855 def failUnlessNodeHasChild(self, node, name):
857 def _check(children):
858 self.failUnless(name in children)
859 d.addCallback(_check)
861 def failIfNodeHasChild(self, node, name):
863 def _check(children):
864 self.failIf(name in children)
865 d.addCallback(_check)
868 def failUnlessChildContentsAre(self, node, name, expected_contents):
869 d = node.get_child_at_path(name)
870 d.addCallback(lambda node: node.download_to_data())
871 def _check(contents):
872 self.failUnlessEqual(contents, expected_contents)
873 d.addCallback(_check)
876 def failUnlessChildURIIs(self, node, name, expected_uri):
877 d = node.get_child_at_path(name)
879 self.failUnlessEqual(child.get_uri(), expected_uri.strip())
880 d.addCallback(_check)
883 def failUnlessURIMatchesChild(self, got_uri, node, name):
884 d = node.get_child_at_path(name)
886 self.failUnlessEqual(got_uri.strip(), child.get_uri())
887 d.addCallback(_check)
890 def failUnlessCHKURIHasContents(self, got_uri, contents):
891 self.failUnless(FakeCHKFileNode.all_contents[got_uri] == contents)
893 def test_PUT_NEWDIRURL_localdir(self):
894 localdir = os.path.abspath("web/PUT_NEWDIRURL_local dir")
895 # create some files there
896 fileutil.make_dirs(os.path.join(localdir, "one"))
897 fileutil.make_dirs(os.path.join(localdir, "one/sub"))
898 fileutil.make_dirs(os.path.join(localdir, "two"))
899 fileutil.make_dirs(os.path.join(localdir, "three"))
900 self.touch(localdir, "three/foo.txt")
901 self.touch(localdir, "three/bar.txt")
902 self.touch(localdir, "zap.zip")
904 d = self.PUT(self.public_url + "/newdir?t=upload&localdir=%s"
905 % urllib.quote(localdir), "")
906 pr = self.public_root
907 d.addCallback(lambda res: self.failUnlessNodeHasChild(pr, "newdir"))
908 d.addCallback(lambda res: pr.get("newdir"))
909 d.addCallback(self.failUnlessNodeKeysAre,
910 ["one", "two", "three", "zap.zip"])
911 d.addCallback(lambda res: pr.get_child_at_path("newdir/one"))
912 d.addCallback(self.failUnlessNodeKeysAre, ["sub"])
913 d.addCallback(lambda res: pr.get_child_at_path("newdir/three"))
914 d.addCallback(self.failUnlessNodeKeysAre, ["foo.txt", "bar.txt"])
915 d.addCallback(lambda res: pr.get_child_at_path("newdir/three/bar.txt"))
916 d.addCallback(lambda barnode: barnode.download_to_data())
917 d.addCallback(lambda contents:
918 self.failUnlessEqual(contents,
919 "contents of three/bar.txt\n"))
922 def test_PUT_NEWDIRURL_localdir_disabled(self):
923 localdir = os.path.abspath("web/PUT_NEWDIRURL_local dir_disabled")
924 # create some files there
925 fileutil.make_dirs(os.path.join(localdir, "one"))
926 fileutil.make_dirs(os.path.join(localdir, "one/sub"))
927 fileutil.make_dirs(os.path.join(localdir, "two"))
928 fileutil.make_dirs(os.path.join(localdir, "three"))
929 self.touch(localdir, "three/foo.txt")
930 self.touch(localdir, "three/bar.txt")
931 self.touch(localdir, "zap.zip")
933 self.disable_local_access()
934 d = self.PUT(self.public_url + "/newdir?t=upload&localdir=%s"
935 % urllib.quote(localdir), "")
936 d.addBoth(self.shouldFail, error.Error, "localfile disabled",
938 "local file access is disabled")
941 def test_PUT_NEWDIRURL_localdir_mkdirs(self):
942 localdir = os.path.abspath("web/PUT_NEWDIRURL_local dir_mkdirs")
943 # create some files there
944 fileutil.make_dirs(os.path.join(localdir, "one"))
945 fileutil.make_dirs(os.path.join(localdir, "one/sub"))
946 fileutil.make_dirs(os.path.join(localdir, "two"))
947 fileutil.make_dirs(os.path.join(localdir, "three"))
948 self.touch(localdir, "three/foo.txt")
949 self.touch(localdir, "three/bar.txt")
950 self.touch(localdir, "zap.zip")
952 d = self.PUT(self.public_url + "/foo/subdir/newdir?t=upload&localdir=%s"
953 % urllib.quote(localdir),
956 d.addCallback(lambda res: self.failUnlessNodeHasChild(fn, "subdir"))
957 d.addCallback(lambda res: fn.get_child_at_path("subdir/newdir"))
958 d.addCallback(self.failUnlessNodeKeysAre,
959 ["one", "two", "three", "zap.zip"])
960 d.addCallback(lambda res: fn.get_child_at_path("subdir/newdir/one"))
961 d.addCallback(self.failUnlessNodeKeysAre, ["sub"])
962 d.addCallback(lambda res: fn.get_child_at_path("subdir/newdir/three"))
963 d.addCallback(self.failUnlessNodeKeysAre, ["foo.txt", "bar.txt"])
964 d.addCallback(lambda res:
965 fn.get_child_at_path("subdir/newdir/three/bar.txt"))
966 d.addCallback(lambda barnode: barnode.download_to_data())
967 d.addCallback(lambda contents:
968 self.failUnlessEqual(contents,
969 "contents of three/bar.txt\n"))
972 def test_PUT_NEWDIRURL_localdir_missing(self):
973 localdir = os.path.abspath("web/PUT_NEWDIRURL_localdir_missing")
974 # we do *not* create it, to trigger an error
975 url = (self.public_url + "/foo/subdir/newdir?t=upload&localdir=%s"
976 % urllib.quote(localdir))
977 d = self.shouldHTTPError2("test_PUT_NEWDIRURL_localdir_missing",
979 "%s doesn't exist!" % localdir,
983 def test_POST_upload(self):
984 d = self.POST(self.public_url + "/foo", t="upload",
985 file=("new.txt", self.NEWFILE_CONTENTS))
987 d.addCallback(self.failUnlessURIMatchesChild, fn, "new.txt")
988 d.addCallback(lambda res:
989 self.failUnlessChildContentsAre(fn, "new.txt",
990 self.NEWFILE_CONTENTS))
993 def test_POST_upload_no_link(self):
994 d = self.POST("/uri", t="upload",
995 file=("new.txt", self.NEWFILE_CONTENTS))
996 def _check_upload_results(page):
997 # this should be a page which describes the results of the upload
998 # that just finished.
999 self.failUnless("Upload Results:" in page)
1000 self.failUnless("URI:" in page)
1001 uri_re = re.compile("URI: <tt><span>(.*)</span>")
1002 mo = uri_re.search(page)
1003 self.failUnless(mo, page)
1004 new_uri = mo.group(1)
1006 d.addCallback(_check_upload_results)
1007 d.addCallback(self.failUnlessCHKURIHasContents, self.NEWFILE_CONTENTS)
1010 def test_POST_upload_no_link_whendone(self):
1011 d = self.POST("/uri", t="upload", when_done="/",
1012 file=("new.txt", self.NEWFILE_CONTENTS))
1013 d.addBoth(self.shouldRedirect, "/")
1014 # XXX Test that resulting welcome page has a "most recent
1015 # upload", the URI of which points to the file contents that
1016 # you just uploaded.
1018 test_POST_upload_no_link_whendone.todo = "Not yet implemented."
1020 def test_POST_upload_no_link_mutable(self):
1021 d = self.POST("/uri", t="upload", mutable="true",
1022 file=("new.txt", self.NEWFILE_CONTENTS))
1023 def _check(new_uri):
1024 new_uri = new_uri.strip()
1026 self.failUnless(IMutableFileURI.providedBy(u))
1027 self.failUnless(u.storage_index in FakeMutableFileNode.all_contents)
1028 n = self.s.create_node_from_uri(new_uri)
1029 return n.download_to_data()
1030 d.addCallback(_check)
1032 self.failUnlessEqual(data, self.NEWFILE_CONTENTS)
1033 d.addCallback(_check2)
1036 def test_POST_upload_mutable(self):
1037 # this creates a mutable file
1038 d = self.POST(self.public_url + "/foo", t="upload", mutable="true",
1039 file=("new.txt", self.NEWFILE_CONTENTS))
1041 d.addCallback(self.failUnlessURIMatchesChild, fn, "new.txt")
1042 d.addCallback(lambda res:
1043 self.failUnlessChildContentsAre(fn, "new.txt",
1044 self.NEWFILE_CONTENTS))
1045 d.addCallback(lambda res: self._foo_node.get("new.txt"))
1047 self.failUnless(IMutableFileNode.providedBy(newnode))
1048 self.failUnless(newnode.is_mutable())
1049 self.failIf(newnode.is_readonly())
1050 self._mutable_uri = newnode.get_uri()
1053 # now upload it again and make sure that the URI doesn't change
1054 NEWER_CONTENTS = self.NEWFILE_CONTENTS + "newer\n"
1055 d.addCallback(lambda res:
1056 self.POST(self.public_url + "/foo", t="upload",
1058 file=("new.txt", NEWER_CONTENTS)))
1059 d.addCallback(self.failUnlessURIMatchesChild, fn, "new.txt")
1060 d.addCallback(lambda res:
1061 self.failUnlessChildContentsAre(fn, "new.txt",
1063 d.addCallback(lambda res: self._foo_node.get("new.txt"))
1065 self.failUnless(IMutableFileNode.providedBy(newnode))
1066 self.failUnless(newnode.is_mutable())
1067 self.failIf(newnode.is_readonly())
1068 self.failUnlessEqual(self._mutable_uri, newnode.get_uri())
1069 d.addCallback(_got2)
1071 # finally list the directory, since mutable files are displayed
1074 d.addCallback(lambda res:
1075 self.GET(self.public_url + "/foo",
1076 followRedirect=True))
1077 def _check_page(res):
1078 # TODO: assert more about the contents
1079 self.failUnless("Overwrite" in res)
1080 self.failUnless("Choose new file:" in res)
1082 d.addCallback(_check_page)
1084 # test that clicking on the "overwrite" button works
1085 EVEN_NEWER_CONTENTS = NEWER_CONTENTS + "even newer\n"
1086 def _parse_overwrite_form_and_submit(res):
1087 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)
1088 mo = OVERWRITE_FORM_RE.search(res)
1090 formaction=mo.group(1)
1091 formname=mo.group(2)
1092 formwhendone=mo.group(3)
1094 if formaction == ".":
1095 formaction = self.public_url + "/foo"
1096 return self.POST(formaction, t="overwrite", name=formname, when_done=formwhendone, file=("new.txt", EVEN_NEWER_CONTENTS), followRedirect=False)
1097 d.addCallback(_parse_overwrite_form_and_submit)
1098 d.addBoth(self.shouldRedirect, urllib.quote(self.public_url + "/foo/"))
1099 d.addCallback(lambda res:
1100 self.failUnlessChildContentsAre(fn, "new.txt",
1101 EVEN_NEWER_CONTENTS))
1102 d.addCallback(lambda res: self._foo_node.get("new.txt"))
1104 self.failUnless(IMutableFileNode.providedBy(newnode))
1105 self.failUnless(newnode.is_mutable())
1106 self.failIf(newnode.is_readonly())
1107 self.failUnlessEqual(self._mutable_uri, newnode.get_uri())
1108 d.addCallback(_got3)
1112 def test_POST_upload_replace(self):
1113 d = self.POST(self.public_url + "/foo", t="upload",
1114 file=("bar.txt", self.NEWFILE_CONTENTS))
1116 d.addCallback(self.failUnlessURIMatchesChild, fn, "bar.txt")
1117 d.addCallback(lambda res:
1118 self.failUnlessChildContentsAre(fn, "bar.txt",
1119 self.NEWFILE_CONTENTS))
1122 def test_POST_upload_no_replace_ok(self):
1123 d = self.POST(self.public_url + "/foo?replace=false", t="upload",
1124 file=("new.txt", self.NEWFILE_CONTENTS))
1125 d.addCallback(lambda res: self.GET(self.public_url + "/foo/new.txt"))
1126 d.addCallback(lambda res: self.failUnlessEqual(res,
1127 self.NEWFILE_CONTENTS))
1130 def test_POST_upload_no_replace_queryarg(self):
1131 d = self.POST(self.public_url + "/foo?replace=false", t="upload",
1132 file=("bar.txt", self.NEWFILE_CONTENTS))
1133 d.addBoth(self.shouldFail, error.Error,
1134 "POST_upload_no_replace_queryarg",
1136 "There was already a child by that name, and you asked me "
1137 "to not replace it")
1138 d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt"))
1139 d.addCallback(self.failUnlessIsBarDotTxt)
1142 def test_POST_upload_no_replace_field(self):
1143 d = self.POST(self.public_url + "/foo", t="upload", replace="false",
1144 file=("bar.txt", self.NEWFILE_CONTENTS))
1145 d.addBoth(self.shouldFail, error.Error, "POST_upload_no_replace_field",
1147 "There was already a child by that name, and you asked me "
1148 "to not replace it")
1149 d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt"))
1150 d.addCallback(self.failUnlessIsBarDotTxt)
1153 def test_POST_upload_whendone(self):
1154 d = self.POST(self.public_url + "/foo", t="upload", when_done="/THERE",
1155 file=("new.txt", self.NEWFILE_CONTENTS))
1156 d.addBoth(self.shouldRedirect, "/THERE")
1158 d.addCallback(lambda res:
1159 self.failUnlessChildContentsAre(fn, "new.txt",
1160 self.NEWFILE_CONTENTS))
1163 def test_POST_upload_named(self):
1165 d = self.POST(self.public_url + "/foo", t="upload",
1166 name="new.txt", file=self.NEWFILE_CONTENTS)
1167 d.addCallback(self.failUnlessURIMatchesChild, fn, "new.txt")
1168 d.addCallback(lambda res:
1169 self.failUnlessChildContentsAre(fn, "new.txt",
1170 self.NEWFILE_CONTENTS))
1173 def test_POST_upload_named_badfilename(self):
1174 d = self.POST(self.public_url + "/foo", t="upload",
1175 name="slashes/are/bad.txt", file=self.NEWFILE_CONTENTS)
1176 d.addBoth(self.shouldFail, error.Error,
1177 "test_POST_upload_named_badfilename",
1179 "name= may not contain a slash",
1181 # make sure that nothing was added
1182 d.addCallback(lambda res:
1183 self.failUnlessNodeKeysAre(self._foo_node,
1184 ["bar.txt", "blockingfile",
1188 def test_POST_mkdir(self): # return value?
1189 d = self.POST(self.public_url + "/foo", t="mkdir", name="newdir")
1190 d.addCallback(lambda res: self._foo_node.get("newdir"))
1191 d.addCallback(self.failUnlessNodeKeysAre, [])
1194 def test_POST_mkdir_no_parentdir_noredirect(self):
1195 d = self.POST("/uri?t=mkdir")
1196 def _after_mkdir(res):
1197 uri.NewDirectoryURI.init_from_string(res)
1198 d.addCallback(_after_mkdir)
1201 def test_POST_mkdir_no_parentdir_redirect(self):
1202 d = self.POST("/uri?t=mkdir&redirect_to_result=true")
1203 d.addBoth(self.shouldRedirect, None, statuscode='303')
1204 def _check_target(target):
1205 target = urllib.unquote(target)
1206 self.failUnless(target.startswith("uri/URI:DIR2:"), target)
1207 d.addCallback(_check_target)
1210 def test_POST_noparent_bad(self):
1211 d = self.shouldHTTPError2("POST /uri?t=bogus", 400, "Bad Request",
1212 "/uri accepts only PUT, PUT?t=mkdir, "
1213 "POST?t=upload, and POST?t=mkdir",
1214 self.POST, "/uri?t=bogus")
1217 def test_welcome_page_mkdir_button(self):
1218 # Fetch the welcome page.
1220 def _after_get_welcome_page(res):
1221 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)
1222 mo = MKDIR_BUTTON_RE.search(res)
1223 formaction = mo.group(1)
1225 formaname = mo.group(3)
1226 formavalue = mo.group(4)
1227 return (formaction, formt, formaname, formavalue)
1228 d.addCallback(_after_get_welcome_page)
1229 def _after_parse_form(res):
1230 (formaction, formt, formaname, formavalue) = res
1231 return self.POST("/%s?t=%s&%s=%s" % (formaction, formt, formaname, formavalue))
1232 d.addCallback(_after_parse_form)
1233 d.addBoth(self.shouldRedirect, None, statuscode='303')
1236 def test_POST_mkdir_replace(self): # return value?
1237 d = self.POST(self.public_url + "/foo", t="mkdir", name="sub")
1238 d.addCallback(lambda res: self._foo_node.get("sub"))
1239 d.addCallback(self.failUnlessNodeKeysAre, [])
1242 def test_POST_mkdir_no_replace_queryarg(self): # return value?
1243 d = self.POST(self.public_url + "/foo?replace=false", t="mkdir", name="sub")
1244 d.addBoth(self.shouldFail, error.Error,
1245 "POST_mkdir_no_replace_queryarg",
1247 "There was already a child by that name, and you asked me "
1248 "to not replace it")
1249 d.addCallback(lambda res: self._foo_node.get("sub"))
1250 d.addCallback(self.failUnlessNodeKeysAre, ["baz.txt"])
1253 def test_POST_mkdir_no_replace_field(self): # return value?
1254 d = self.POST(self.public_url + "/foo", t="mkdir", name="sub",
1256 d.addBoth(self.shouldFail, error.Error, "POST_mkdir_no_replace_field",
1258 "There was already a child by that name, and you asked me "
1259 "to not replace it")
1260 d.addCallback(lambda res: self._foo_node.get("sub"))
1261 d.addCallback(self.failUnlessNodeKeysAre, ["baz.txt"])
1264 def test_POST_mkdir_whendone_field(self):
1265 d = self.POST(self.public_url + "/foo",
1266 t="mkdir", name="newdir", when_done="/THERE")
1267 d.addBoth(self.shouldRedirect, "/THERE")
1268 d.addCallback(lambda res: self._foo_node.get("newdir"))
1269 d.addCallback(self.failUnlessNodeKeysAre, [])
1272 def test_POST_mkdir_whendone_queryarg(self):
1273 d = self.POST(self.public_url + "/foo?when_done=/THERE",
1274 t="mkdir", name="newdir")
1275 d.addBoth(self.shouldRedirect, "/THERE")
1276 d.addCallback(lambda res: self._foo_node.get("newdir"))
1277 d.addCallback(self.failUnlessNodeKeysAre, [])
1280 def test_POST_put_uri(self):
1281 contents, n, newuri = self.makefile(8)
1282 d = self.POST(self.public_url + "/foo", t="uri", name="new.txt", uri=newuri)
1283 d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, "new.txt")
1284 d.addCallback(lambda res:
1285 self.failUnlessChildContentsAre(self._foo_node, "new.txt",
1289 def test_POST_put_uri_replace(self):
1290 contents, n, newuri = self.makefile(8)
1291 d = self.POST(self.public_url + "/foo", t="uri", name="bar.txt", uri=newuri)
1292 d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, "bar.txt")
1293 d.addCallback(lambda res:
1294 self.failUnlessChildContentsAre(self._foo_node, "bar.txt",
1298 def test_POST_put_uri_no_replace_queryarg(self):
1299 contents, n, newuri = self.makefile(8)
1300 d = self.POST(self.public_url + "/foo?replace=false", t="uri",
1301 name="bar.txt", uri=newuri)
1302 d.addBoth(self.shouldFail, error.Error,
1303 "POST_put_uri_no_replace_queryarg",
1305 "There was already a child by that name, and you asked me "
1306 "to not replace it")
1307 d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt"))
1308 d.addCallback(self.failUnlessIsBarDotTxt)
1311 def test_POST_put_uri_no_replace_field(self):
1312 contents, n, newuri = self.makefile(8)
1313 d = self.POST(self.public_url + "/foo", t="uri", replace="false",
1314 name="bar.txt", uri=newuri)
1315 d.addBoth(self.shouldFail, error.Error,
1316 "POST_put_uri_no_replace_field",
1318 "There was already a child by that name, and you asked me "
1319 "to not replace it")
1320 d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt"))
1321 d.addCallback(self.failUnlessIsBarDotTxt)
1324 def test_POST_delete(self):
1325 d = self.POST(self.public_url + "/foo", t="delete", name="bar.txt")
1326 d.addCallback(lambda res: self._foo_node.list())
1327 def _check(children):
1328 self.failIf("bar.txt" in children)
1329 d.addCallback(_check)
1332 def test_POST_rename_file(self):
1333 d = self.POST(self.public_url + "/foo", t="rename",
1334 from_name="bar.txt", to_name='wibble.txt')
1335 d.addCallback(lambda res:
1336 self.failIfNodeHasChild(self._foo_node, "bar.txt"))
1337 d.addCallback(lambda res:
1338 self.failUnlessNodeHasChild(self._foo_node, "wibble.txt"))
1339 d.addCallback(lambda res: self.GET(self.public_url + "/foo/wibble.txt"))
1340 d.addCallback(self.failUnlessIsBarDotTxt)
1341 d.addCallback(lambda res: self.GET(self.public_url + "/foo/wibble.txt?t=json"))
1342 d.addCallback(self.failUnlessIsBarJSON)
1345 def test_POST_rename_file_replace(self):
1346 # rename a file and replace a directory with it
1347 d = self.POST(self.public_url + "/foo", t="rename",
1348 from_name="bar.txt", to_name='empty')
1349 d.addCallback(lambda res:
1350 self.failIfNodeHasChild(self._foo_node, "bar.txt"))
1351 d.addCallback(lambda res:
1352 self.failUnlessNodeHasChild(self._foo_node, "empty"))
1353 d.addCallback(lambda res: self.GET(self.public_url + "/foo/empty"))
1354 d.addCallback(self.failUnlessIsBarDotTxt)
1355 d.addCallback(lambda res: self.GET(self.public_url + "/foo/empty?t=json"))
1356 d.addCallback(self.failUnlessIsBarJSON)
1359 def test_POST_rename_file_no_replace_queryarg(self):
1360 # rename a file and replace a directory with it
1361 d = self.POST(self.public_url + "/foo?replace=false", t="rename",
1362 from_name="bar.txt", to_name='empty')
1363 d.addBoth(self.shouldFail, error.Error,
1364 "POST_rename_file_no_replace_queryarg",
1366 "There was already a child by that name, and you asked me "
1367 "to not replace it")
1368 d.addCallback(lambda res: self.GET(self.public_url + "/foo/empty?t=json"))
1369 d.addCallback(self.failUnlessIsEmptyJSON)
1372 def test_POST_rename_file_no_replace_field(self):
1373 # rename a file and replace a directory with it
1374 d = self.POST(self.public_url + "/foo", t="rename", replace="false",
1375 from_name="bar.txt", to_name='empty')
1376 d.addBoth(self.shouldFail, error.Error,
1377 "POST_rename_file_no_replace_field",
1379 "There was already a child by that name, and you asked me "
1380 "to not replace it")
1381 d.addCallback(lambda res: self.GET(self.public_url + "/foo/empty?t=json"))
1382 d.addCallback(self.failUnlessIsEmptyJSON)
1385 def failUnlessIsEmptyJSON(self, res):
1386 data = simplejson.loads(res)
1387 self.failUnlessEqual(data[0], "dirnode", data)
1388 self.failUnlessEqual(len(data[1]["children"]), 0)
1390 def test_POST_rename_file_slash_fail(self):
1391 d = self.POST(self.public_url + "/foo", t="rename",
1392 from_name="bar.txt", to_name='kirk/spock.txt')
1393 d.addBoth(self.shouldFail, error.Error,
1394 "test_POST_rename_file_slash_fail",
1396 "to_name= may not contain a slash",
1398 d.addCallback(lambda res:
1399 self.failUnlessNodeHasChild(self._foo_node, "bar.txt"))
1400 d.addCallback(lambda res: self.POST(self.public_url, t="rename",
1401 from_name="foo/bar.txt", to_name='george.txt'))
1402 d.addBoth(self.shouldFail, error.Error,
1403 "test_POST_rename_file_slash_fail",
1405 "from_name= may not contain a slash",
1407 d.addCallback(lambda res:
1408 self.failUnlessNodeHasChild(self.public_root, "foo"))
1409 d.addCallback(lambda res:
1410 self.failIfNodeHasChild(self.public_root, "george.txt"))
1411 d.addCallback(lambda res:
1412 self.failUnlessNodeHasChild(self._foo_node, "bar.txt"))
1413 d.addCallback(lambda res: self.GET(self.public_url + "/foo?t=json"))
1414 d.addCallback(self.failUnlessIsFooJSON)
1417 def test_POST_rename_dir(self):
1418 d = self.POST(self.public_url, t="rename",
1419 from_name="foo", to_name='plunk')
1420 d.addCallback(lambda res:
1421 self.failIfNodeHasChild(self.public_root, "foo"))
1422 d.addCallback(lambda res:
1423 self.failUnlessNodeHasChild(self.public_root, "plunk"))
1424 d.addCallback(lambda res: self.GET(self.public_url + "/plunk?t=json"))
1425 d.addCallback(self.failUnlessIsFooJSON)
1428 def shouldRedirect(self, res, target=None, statuscode=None):
1429 """ If target is not None then the redirection has to go to target. If
1430 statuscode is not None then the redirection has to be accomplished with
1431 that HTTP status code."""
1432 if not isinstance(res, failure.Failure):
1433 self.fail("we were expecting to get redirected %s, not get an"
1434 " actual page: %s" % ((target is None) and "somewhere" or ("to " + target), res))
1435 res.trap(error.PageRedirect)
1436 if statuscode is not None:
1437 self.failUnlessEqual(res.value.status, statuscode)
1438 if target is not None:
1439 # the PageRedirect does not seem to capture the uri= query arg
1440 # properly, so we can't check for it.
1441 realtarget = self.webish_url + target
1442 self.failUnlessEqual(res.value.location, realtarget)
1443 return res.value.location
1445 def test_GET_URI_form(self):
1446 base = "/uri?uri=%s" % self._bar_txt_uri
1447 # this is supposed to give us a redirect to /uri/$URI, plus arguments
1448 targetbase = "/uri/%s" % urllib.quote(self._bar_txt_uri)
1450 d.addBoth(self.shouldRedirect, targetbase)
1451 d.addCallback(lambda res: self.GET(base+"&filename=bar.txt"))
1452 d.addBoth(self.shouldRedirect, targetbase+"?filename=bar.txt")
1453 d.addCallback(lambda res: self.GET(base+"&t=json"))
1454 d.addBoth(self.shouldRedirect, targetbase+"?t=json")
1455 d.addCallback(self.log, "about to get file by uri")
1456 d.addCallback(lambda res: self.GET(base, followRedirect=True))
1457 d.addCallback(self.failUnlessIsBarDotTxt)
1458 d.addCallback(self.log, "got file by uri, about to get dir by uri")
1459 d.addCallback(lambda res: self.GET("/uri?uri=%s&t=json" % self._foo_uri,
1460 followRedirect=True))
1461 d.addCallback(self.failUnlessIsFooJSON)
1462 d.addCallback(self.log, "got dir by uri")
1466 def test_GET_rename_form(self):
1467 d = self.GET(self.public_url + "/foo?t=rename-form&name=bar.txt",
1468 followRedirect=True) # XXX [ ] todo: figure out why '.../foo' doesn't work
1470 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,))
1471 self.failUnless(re.search(r'name="from_name" value="bar\.txt"', res))
1472 d.addCallback(_check)
1475 def log(self, res, msg):
1476 #print "MSG: %s RES: %s" % (msg, res)
1480 def test_GET_URI_URL(self):
1481 base = "/uri/%s" % self._bar_txt_uri
1483 d.addCallback(self.failUnlessIsBarDotTxt)
1484 d.addCallback(lambda res: self.GET(base+"?filename=bar.txt"))
1485 d.addCallback(self.failUnlessIsBarDotTxt)
1486 d.addCallback(lambda res: self.GET(base+"?filename=bar.txt&save=true"))
1487 d.addCallback(self.failUnlessIsBarDotTxt)
1490 def test_GET_URI_URL_dir(self):
1491 base = "/uri/%s?t=json" % self._foo_uri
1493 d.addCallback(self.failUnlessIsFooJSON)
1496 def test_GET_URI_URL_missing(self):
1497 base = "/uri/%s" % self._bad_file_uri
1499 d.addBoth(self.shouldHTTPError, "test_GET_URI_URL_missing",
1500 http.GONE, response_substring="NotEnoughPeersError")
1501 # TODO: how can we exercise both sides of WebDownloadTarget.fail
1502 # here? we must arrange for a download to fail after target.open()
1503 # has been called, and then inspect the response to see that it is
1504 # shorter than we expected.
1507 def test_PUT_NEWFILEURL_uri(self):
1508 contents, n, new_uri = self.makefile(8)
1509 d = self.PUT(self.public_url + "/foo/new.txt?t=uri", new_uri)
1510 d.addCallback(lambda res: self.failUnlessEqual(res.strip(), new_uri))
1511 d.addCallback(lambda res:
1512 self.failUnlessChildContentsAre(self._foo_node, "new.txt",
1516 def test_PUT_NEWFILEURL_uri_replace(self):
1517 contents, n, new_uri = self.makefile(8)
1518 d = self.PUT(self.public_url + "/foo/bar.txt?t=uri", new_uri)
1519 d.addCallback(lambda res: self.failUnlessEqual(res.strip(), new_uri))
1520 d.addCallback(lambda res:
1521 self.failUnlessChildContentsAre(self._foo_node, "bar.txt",
1525 def test_PUT_NEWFILEURL_uri_no_replace(self):
1526 contents, n, new_uri = self.makefile(8)
1527 d = self.PUT(self.public_url + "/foo/bar.txt?t=uri&replace=false", new_uri)
1528 d.addBoth(self.shouldFail, error.Error, "PUT_NEWFILEURL_uri_no_replace",
1530 "There was already a child by that name, and you asked me "
1531 "to not replace it")
1534 def test_PUT_NEWFILE_URI(self):
1535 file_contents = "New file contents here\n"
1536 d = self.PUT("/uri", file_contents)
1538 self.failUnless(uri in FakeCHKFileNode.all_contents)
1539 self.failUnlessEqual(FakeCHKFileNode.all_contents[uri],
1541 return self.GET("/uri/%s" % uri)
1542 d.addCallback(_check)
1544 self.failUnlessEqual(res, file_contents)
1545 d.addCallback(_check2)
1548 def test_PUT_NEWFILE_URI_only_PUT(self):
1549 d = self.PUT("/uri?t=bogus", "")
1550 d.addBoth(self.shouldFail, error.Error,
1551 "PUT_NEWFILE_URI_only_PUT",
1553 "/uri only accepts PUT and PUT?t=mkdir")
1556 def test_PUT_NEWFILE_URI_mutable(self):
1557 file_contents = "New file contents here\n"
1558 d = self.PUT("/uri?mutable=true", file_contents)
1559 def _check_mutable(uri):
1562 self.failUnless(IMutableFileURI.providedBy(u))
1563 self.failUnless(u.storage_index in FakeMutableFileNode.all_contents)
1564 n = self.s.create_node_from_uri(uri)
1565 return n.download_to_data()
1566 d.addCallback(_check_mutable)
1567 def _check2_mutable(data):
1568 self.failUnlessEqual(data, file_contents)
1569 d.addCallback(_check2_mutable)
1573 self.failUnless(uri in FakeCHKFileNode.all_contents)
1574 self.failUnlessEqual(FakeCHKFileNode.all_contents[uri],
1576 return self.GET("/uri/%s" % uri)
1577 d.addCallback(_check)
1579 self.failUnlessEqual(res, file_contents)
1580 d.addCallback(_check2)
1583 def test_PUT_mkdir(self):
1584 d = self.PUT("/uri?t=mkdir", "")
1586 n = self.s.create_node_from_uri(uri.strip())
1587 d2 = self.failUnlessNodeKeysAre(n, [])
1588 d2.addCallback(lambda res:
1589 self.GET("/uri/%s?t=json" % uri))
1591 d.addCallback(_check)
1592 d.addCallback(self.failUnlessIsEmptyJSON)
1595 def test_POST_check(self):
1596 d = self.POST(self.public_url + "/foo", t="check", name="bar.txt")
1598 # this returns a string form of the results, which are probably
1599 # None since we're using fake filenodes.
1600 # TODO: verify that the check actually happened, by changing
1601 # FakeCHKFileNode to count how many times .check() has been
1604 d.addCallback(_done)
1607 def test_bad_method(self):
1608 url = self.webish_url + self.public_url + "/foo/bar.txt"
1609 d = self.shouldHTTPError2("test_bad_method", 404, "Not Found", None,
1610 client.getPage, url, method="BOGUS")
1613 def test_short_url(self):
1614 url = self.webish_url + "/uri"
1615 d = self.shouldHTTPError2("test_short_url", 404, "Not Found", None,
1616 client.getPage, url, method="DELETE")