2 from twisted.trial import unittest
3 from twisted.python import usage
5 from allmydata.util import fileutil
6 from allmydata.scripts.common import get_aliases
7 from allmydata.scripts import cli
8 from allmydata.test.no_network import GridTestMixin
9 from allmydata.util.encodingutil import get_io_encoding, unicode_to_argv
10 from allmydata.util.fileutil import abspath_expanduser_unicode
11 from .test_cli import CLITestMixin
13 timeout = 480 # deep_check takes 360s on Zandr's linksys box, others take > 240s
15 class Put(GridTestMixin, CLITestMixin, unittest.TestCase):
17 def test_unlinked_immutable_stdin(self):
18 # tahoe get `echo DATA | tahoe put`
19 # tahoe get `echo DATA | tahoe put -`
20 self.basedir = "cli/Put/unlinked_immutable_stdin"
23 d = self.do_cli("put", stdin=DATA)
26 self.failUnlessIn("waiting for file data on stdin..", err)
27 self.failUnlessIn("200 OK", err)
29 self.failUnless(self.readcap.startswith("URI:CHK:"))
30 d.addCallback(_uploaded)
31 d.addCallback(lambda res: self.do_cli("get", self.readcap))
34 self.failUnlessReallyEqual(err, "")
35 self.failUnlessReallyEqual(out, DATA)
36 d.addCallback(_downloaded)
37 d.addCallback(lambda res: self.do_cli("put", "-", stdin=DATA))
38 d.addCallback(lambda (rc, out, err):
39 self.failUnlessReallyEqual(out, self.readcap))
42 def test_unlinked_immutable_from_file(self):
44 # tahoe put ./file.txt
45 # tahoe put /tmp/file.txt
46 # tahoe put ~/file.txt
47 self.basedir = "cli/Put/unlinked_immutable_from_file"
50 rel_fn = os.path.join(self.basedir, "DATAFILE")
51 abs_fn = unicode_to_argv(abspath_expanduser_unicode(unicode(rel_fn)))
52 # we make the file small enough to fit in a LIT file, for speed
53 fileutil.write(rel_fn, "short file")
54 d = self.do_cli("put", rel_fn)
55 def _uploaded((rc, out, err)):
57 self.failUnless(readcap.startswith("URI:LIT:"), readcap)
58 self.readcap = readcap
59 d.addCallback(_uploaded)
60 d.addCallback(lambda res: self.do_cli("put", "./" + rel_fn))
61 d.addCallback(lambda (rc,stdout,stderr):
62 self.failUnlessReallyEqual(stdout, self.readcap))
63 d.addCallback(lambda res: self.do_cli("put", abs_fn))
64 d.addCallback(lambda (rc,stdout,stderr):
65 self.failUnlessReallyEqual(stdout, self.readcap))
66 # we just have to assume that ~ is handled properly
69 def test_immutable_from_file(self):
70 # tahoe put file.txt uploaded.txt
71 # tahoe - uploaded.txt
72 # tahoe put file.txt subdir/uploaded.txt
73 # tahoe put file.txt tahoe:uploaded.txt
74 # tahoe put file.txt tahoe:subdir/uploaded.txt
75 # tahoe put file.txt DIRCAP:./uploaded.txt
76 # tahoe put file.txt DIRCAP:./subdir/uploaded.txt
77 self.basedir = "cli/Put/immutable_from_file"
80 rel_fn = os.path.join(self.basedir, "DATAFILE")
81 # we make the file small enough to fit in a LIT file, for speed
83 DATA2 = "short file two"
84 fileutil.write(rel_fn, DATA)
86 d = self.do_cli("create-alias", "tahoe")
88 d.addCallback(lambda res:
89 self.do_cli("put", rel_fn, "uploaded.txt"))
90 def _uploaded((rc, out, err)):
92 self.failUnless(readcap.startswith("URI:LIT:"), readcap)
93 self.failUnlessIn("201 Created", err)
94 self.readcap = readcap
95 d.addCallback(_uploaded)
96 d.addCallback(lambda res:
97 self.do_cli("get", "tahoe:uploaded.txt"))
98 d.addCallback(lambda (rc,stdout,stderr):
99 self.failUnlessReallyEqual(stdout, DATA))
101 d.addCallback(lambda res:
102 self.do_cli("put", "-", "uploaded.txt", stdin=DATA2))
103 def _replaced((rc, out, err)):
104 readcap = out.strip()
105 self.failUnless(readcap.startswith("URI:LIT:"), readcap)
106 self.failUnlessIn("200 OK", err)
107 d.addCallback(_replaced)
109 d.addCallback(lambda res:
110 self.do_cli("put", rel_fn, "subdir/uploaded2.txt"))
111 d.addCallback(lambda res: self.do_cli("get", "subdir/uploaded2.txt"))
112 d.addCallback(lambda (rc,stdout,stderr):
113 self.failUnlessReallyEqual(stdout, DATA))
115 d.addCallback(lambda res:
116 self.do_cli("put", rel_fn, "tahoe:uploaded3.txt"))
117 d.addCallback(lambda res: self.do_cli("get", "tahoe:uploaded3.txt"))
118 d.addCallback(lambda (rc,stdout,stderr):
119 self.failUnlessReallyEqual(stdout, DATA))
121 d.addCallback(lambda res:
122 self.do_cli("put", rel_fn, "tahoe:subdir/uploaded4.txt"))
123 d.addCallback(lambda res:
124 self.do_cli("get", "tahoe:subdir/uploaded4.txt"))
125 d.addCallback(lambda (rc,stdout,stderr):
126 self.failUnlessReallyEqual(stdout, DATA))
128 def _get_dircap(res):
129 self.dircap = get_aliases(self.get_clientdir())["tahoe"]
130 d.addCallback(_get_dircap)
132 d.addCallback(lambda res:
133 self.do_cli("put", rel_fn,
134 self.dircap+":./uploaded5.txt"))
135 d.addCallback(lambda res:
136 self.do_cli("get", "tahoe:uploaded5.txt"))
137 d.addCallback(lambda (rc,stdout,stderr):
138 self.failUnlessReallyEqual(stdout, DATA))
140 d.addCallback(lambda res:
141 self.do_cli("put", rel_fn,
142 self.dircap+":./subdir/uploaded6.txt"))
143 d.addCallback(lambda res:
144 self.do_cli("get", "tahoe:subdir/uploaded6.txt"))
145 d.addCallback(lambda (rc,stdout,stderr):
146 self.failUnlessReallyEqual(stdout, DATA))
150 def test_mutable_unlinked(self):
151 # FILECAP = `echo DATA | tahoe put --mutable`
152 # tahoe get FILECAP, compare against DATA
153 # echo DATA2 | tahoe put - FILECAP
154 # tahoe get FILECAP, compare against DATA2
155 # tahoe put file.txt FILECAP
156 self.basedir = "cli/Put/mutable_unlinked"
161 rel_fn = os.path.join(self.basedir, "DATAFILE")
162 DATA3 = "three" * 100
163 fileutil.write(rel_fn, DATA3)
165 d = self.do_cli("put", "--mutable", stdin=DATA)
168 self.failUnlessIn("waiting for file data on stdin..", err)
169 self.failUnlessIn("200 OK", err)
171 self.failUnless(self.filecap.startswith("URI:SSK:"), self.filecap)
172 d.addCallback(_created)
173 d.addCallback(lambda res: self.do_cli("get", self.filecap))
174 d.addCallback(lambda (rc,out,err): self.failUnlessReallyEqual(out, DATA))
176 d.addCallback(lambda res: self.do_cli("put", "-", self.filecap, stdin=DATA2))
179 self.failUnlessIn("waiting for file data on stdin..", err)
180 self.failUnlessIn("200 OK", err)
181 self.failUnlessReallyEqual(self.filecap, out)
182 d.addCallback(_replaced)
183 d.addCallback(lambda res: self.do_cli("get", self.filecap))
184 d.addCallback(lambda (rc,out,err): self.failUnlessReallyEqual(out, DATA2))
186 d.addCallback(lambda res: self.do_cli("put", rel_fn, self.filecap))
189 self.failUnlessIn("200 OK", err)
190 self.failUnlessReallyEqual(self.filecap, out)
191 d.addCallback(_replaced2)
192 d.addCallback(lambda res: self.do_cli("get", self.filecap))
193 d.addCallback(lambda (rc,out,err): self.failUnlessReallyEqual(out, DATA3))
197 def test_mutable(self):
198 # echo DATA1 | tahoe put --mutable - uploaded.txt
199 # echo DATA2 | tahoe put - uploaded.txt # should modify-in-place
200 # tahoe get uploaded.txt, compare against DATA2
202 self.basedir = "cli/Put/mutable"
206 fn1 = os.path.join(self.basedir, "DATA1")
207 fileutil.write(fn1, DATA1)
209 fn2 = os.path.join(self.basedir, "DATA2")
210 fileutil.write(fn2, DATA2)
212 d = self.do_cli("create-alias", "tahoe")
213 d.addCallback(lambda res:
214 self.do_cli("put", "--mutable", fn1, "tahoe:uploaded.txt"))
217 self.failUnlessEqual(rc, 0, str(res))
218 self.failUnlessEqual(err.strip(), "201 Created", str(res))
220 d.addCallback(_check)
221 d.addCallback(lambda res:
222 self.do_cli("put", fn2, "tahoe:uploaded.txt"))
225 self.failUnlessEqual(rc, 0, str(res))
226 self.failUnlessEqual(err.strip(), "200 OK", str(res))
227 self.failUnlessEqual(out, self.uri, str(res))
228 d.addCallback(_check2)
229 d.addCallback(lambda res:
230 self.do_cli("get", "tahoe:uploaded.txt"))
231 d.addCallback(lambda (rc,out,err): self.failUnlessReallyEqual(out, DATA2))
234 def _check_mdmf_json(self, (rc, json, err)):
235 self.failUnlessEqual(rc, 0)
236 self.failUnlessEqual(err, "")
237 self.failUnlessIn('"format": "MDMF"', json)
238 # We also want a valid MDMF cap to be in the json.
239 self.failUnlessIn("URI:MDMF", json)
240 self.failUnlessIn("URI:MDMF-RO", json)
241 self.failUnlessIn("URI:MDMF-Verifier", json)
243 def _check_sdmf_json(self, (rc, json, err)):
244 self.failUnlessEqual(rc, 0)
245 self.failUnlessEqual(err, "")
246 self.failUnlessIn('"format": "SDMF"', json)
247 # We also want to see the appropriate SDMF caps.
248 self.failUnlessIn("URI:SSK", json)
249 self.failUnlessIn("URI:SSK-RO", json)
250 self.failUnlessIn("URI:SSK-Verifier", json)
252 def _check_chk_json(self, (rc, json, err)):
253 self.failUnlessEqual(rc, 0)
254 self.failUnlessEqual(err, "")
255 self.failUnlessIn('"format": "CHK"', json)
256 # We also want to see the appropriate CHK caps.
257 self.failUnlessIn("URI:CHK", json)
258 self.failUnlessIn("URI:CHK-Verifier", json)
260 def test_format(self):
261 self.basedir = "cli/Put/format"
263 data = "data" * 40000 # 160kB total, two segments
264 fn1 = os.path.join(self.basedir, "data")
265 fileutil.write(fn1, data)
266 d = self.do_cli("create-alias", "tahoe")
268 def _put_and_ls(ign, cmdargs, expected, filename=None):
270 args = ["put"] + cmdargs + [fn1, filename]
273 args = ["put"] + cmdargs + [fn1]
274 d2 = self.do_cli(*args)
275 def _list((rc, out, err)):
276 self.failUnlessEqual(rc, 0) # don't allow failure
278 return self.do_cli("ls", "--json", filename)
281 return self.do_cli("ls", "--json", cap)
282 d2.addCallback(_list)
285 # 'tahoe put' to a directory
286 d.addCallback(_put_and_ls, ["--mutable"], "SDMF", "tahoe:s1.txt")
287 d.addCallback(self._check_sdmf_json) # backwards-compatibility
288 d.addCallback(_put_and_ls, ["--format=SDMF"], "SDMF", "tahoe:s2.txt")
289 d.addCallback(self._check_sdmf_json)
290 d.addCallback(_put_and_ls, ["--format=sdmf"], "SDMF", "tahoe:s3.txt")
291 d.addCallback(self._check_sdmf_json)
292 d.addCallback(_put_and_ls, ["--mutable", "--format=SDMF"], "SDMF", "tahoe:s4.txt")
293 d.addCallback(self._check_sdmf_json)
295 d.addCallback(_put_and_ls, ["--format=MDMF"], "MDMF", "tahoe:m1.txt")
296 d.addCallback(self._check_mdmf_json)
297 d.addCallback(_put_and_ls, ["--mutable", "--format=MDMF"], "MDMF", "tahoe:m2.txt")
298 d.addCallback(self._check_mdmf_json)
300 d.addCallback(_put_and_ls, ["--format=CHK"], "CHK", "tahoe:c1.txt")
301 d.addCallback(self._check_chk_json)
302 d.addCallback(_put_and_ls, [], "CHK", "tahoe:c1.txt")
303 d.addCallback(self._check_chk_json)
305 # 'tahoe put' unlinked
306 d.addCallback(_put_and_ls, ["--mutable"], "SDMF")
307 d.addCallback(self._check_sdmf_json) # backwards-compatibility
308 d.addCallback(_put_and_ls, ["--format=SDMF"], "SDMF")
309 d.addCallback(self._check_sdmf_json)
310 d.addCallback(_put_and_ls, ["--format=sdmf"], "SDMF")
311 d.addCallback(self._check_sdmf_json)
312 d.addCallback(_put_and_ls, ["--mutable", "--format=SDMF"], "SDMF")
313 d.addCallback(self._check_sdmf_json)
315 d.addCallback(_put_and_ls, ["--format=MDMF"], "MDMF")
316 d.addCallback(self._check_mdmf_json)
317 d.addCallback(_put_and_ls, ["--mutable", "--format=MDMF"], "MDMF")
318 d.addCallback(self._check_mdmf_json)
320 d.addCallback(_put_and_ls, ["--format=CHK"], "CHK")
321 d.addCallback(self._check_chk_json)
322 d.addCallback(_put_and_ls, [], "CHK")
323 d.addCallback(self._check_chk_json)
327 def test_put_to_mdmf_cap(self):
328 self.basedir = "cli/Put/put_to_mdmf_cap"
330 data = "data" * 100000
331 fn1 = os.path.join(self.basedir, "data")
332 fileutil.write(fn1, data)
333 d = self.do_cli("put", "--format=MDMF", fn1)
334 def _got_cap((rc, out, err)):
335 self.failUnlessEqual(rc, 0)
336 self.cap = out.strip()
337 d.addCallback(_got_cap)
338 # Now try to write something to the cap using put.
339 data2 = "data2" * 100000
340 fn2 = os.path.join(self.basedir, "data2")
341 fileutil.write(fn2, data2)
342 d.addCallback(lambda ignored:
343 self.do_cli("put", fn2, self.cap))
344 def _got_put((rc, out, err)):
345 self.failUnlessEqual(rc, 0)
346 self.failUnlessIn(self.cap, out)
347 d.addCallback(_got_put)
348 # Now get the cap. We should see the data we just put there.
349 d.addCallback(lambda ignored:
350 self.do_cli("get", self.cap))
351 def _got_data((rc, out, err)):
352 self.failUnlessEqual(rc, 0)
353 self.failUnlessEqual(out, data2)
354 d.addCallback(_got_data)
355 # add some extension information to the cap and try to put something
357 def _make_extended_cap(ignored):
358 self.cap = self.cap + ":Extension-Stuff"
359 d.addCallback(_make_extended_cap)
360 data3 = "data3" * 100000
361 fn3 = os.path.join(self.basedir, "data3")
362 fileutil.write(fn3, data3)
363 d.addCallback(lambda ignored:
364 self.do_cli("put", fn3, self.cap))
365 d.addCallback(lambda ignored:
366 self.do_cli("get", self.cap))
367 def _got_data3((rc, out, err)):
368 self.failUnlessEqual(rc, 0)
369 self.failUnlessEqual(out, data3)
370 d.addCallback(_got_data3)
373 def test_put_to_sdmf_cap(self):
374 self.basedir = "cli/Put/put_to_sdmf_cap"
376 data = "data" * 100000
377 fn1 = os.path.join(self.basedir, "data")
378 fileutil.write(fn1, data)
379 d = self.do_cli("put", "--format=SDMF", fn1)
380 def _got_cap((rc, out, err)):
381 self.failUnlessEqual(rc, 0)
382 self.cap = out.strip()
383 d.addCallback(_got_cap)
384 # Now try to write something to the cap using put.
385 data2 = "data2" * 100000
386 fn2 = os.path.join(self.basedir, "data2")
387 fileutil.write(fn2, data2)
388 d.addCallback(lambda ignored:
389 self.do_cli("put", fn2, self.cap))
390 def _got_put((rc, out, err)):
391 self.failUnlessEqual(rc, 0)
392 self.failUnlessIn(self.cap, out)
393 d.addCallback(_got_put)
394 # Now get the cap. We should see the data we just put there.
395 d.addCallback(lambda ignored:
396 self.do_cli("get", self.cap))
397 def _got_data((rc, out, err)):
398 self.failUnlessEqual(rc, 0)
399 self.failUnlessEqual(out, data2)
400 d.addCallback(_got_data)
403 def test_mutable_type_invalid_format(self):
405 self.failUnlessRaises(usage.UsageError,
409 def test_put_with_nonexistent_alias(self):
410 # when invoked with an alias that doesn't exist, 'tahoe put'
411 # should output a useful error message, not a stack trace
412 self.basedir = "cli/Put/put_with_nonexistent_alias"
414 d = self.do_cli("put", "somefile", "fake:afile")
415 def _check((rc, out, err)):
416 self.failUnlessReallyEqual(rc, 1)
417 self.failUnlessIn("error:", err)
418 self.failUnlessReallyEqual(out, "")
419 d.addCallback(_check)
422 def test_immutable_from_file_unicode(self):
423 # tahoe put "\u00E0 trier.txt" "\u00E0 trier.txt"
426 a_trier_arg = u"\u00E0 trier.txt".encode(get_io_encoding())
427 except UnicodeEncodeError:
428 raise unittest.SkipTest("A non-ASCII command argument could not be encoded on this platform.")
430 self.skip_if_cannot_represent_filename(u"\u00E0 trier.txt")
432 self.basedir = "cli/Put/immutable_from_file_unicode"
435 rel_fn = os.path.join(unicode(self.basedir), u"\u00E0 trier.txt")
436 # we make the file small enough to fit in a LIT file, for speed
438 fileutil.write(rel_fn, DATA)
440 d = self.do_cli("create-alias", "tahoe")
442 d.addCallback(lambda res:
443 self.do_cli("put", rel_fn.encode(get_io_encoding()), a_trier_arg))
444 def _uploaded((rc, out, err)):
445 readcap = out.strip()
446 self.failUnless(readcap.startswith("URI:LIT:"), readcap)
447 self.failUnlessIn("201 Created", err)
448 self.readcap = readcap
449 d.addCallback(_uploaded)
451 d.addCallback(lambda res:
452 self.do_cli("get", "tahoe:" + a_trier_arg))
453 d.addCallback(lambda (rc, out, err):
454 self.failUnlessReallyEqual(out, DATA))