source: trunk/src/allmydata/test/cli/test_list.py

Last change on this file was dfdb6c60, checked in by Itamar Turner-Trauring <itamar@…>, at 2024-03-01T17:17:11Z

Fix lints.

  • Property mode set to 100644
File size: 12.7 KB
Line 
1"""
2Ported to Python 3.
3"""
4
5from twisted.trial import unittest
6from twisted.internet import defer
7
8from allmydata.immutable import upload
9from allmydata.interfaces import MDMF_VERSION, SDMF_VERSION
10from allmydata.mutable.publish import MutableData
11from ..no_network import GridTestMixin
12from allmydata.util.encodingutil import quote_output
13from .common import CLITestMixin
14
15
16class List(GridTestMixin, CLITestMixin, unittest.TestCase):
17    def test_list(self):
18        self.basedir = "cli/List/list"
19        self.set_up_grid()
20        c0 = self.g.clients[0]
21        small = b"small"
22
23        good_arg = u"g\u00F6\u00F6d"
24        good_out = u"g\u00F6\u00F6d"
25
26        d = c0.create_dirnode()
27        def _stash_root_and_create_file(n):
28            self.rootnode = n
29            self.rooturi = str(n.get_uri(), "utf-8")
30            return n.add_file(u"g\u00F6\u00F6d", upload.Data(small, convergence=b""))
31        d.addCallback(_stash_root_and_create_file)
32        def _stash_goodcap(n):
33            self.goodcap = n.get_uri()
34        d.addCallback(_stash_goodcap)
35        d.addCallback(lambda ign: self.rootnode.create_subdirectory(u"1share"))
36        d.addCallback(lambda n:
37                      self.delete_shares_numbered(n.get_uri(), list(range(1,10))))
38        d.addCallback(lambda ign: self.rootnode.create_subdirectory(u"0share"))
39        d.addCallback(lambda n:
40                      self.delete_shares_numbered(n.get_uri(), list(range(0,10))))
41        d.addCallback(lambda ign:
42                      self.do_cli("add-alias", "tahoe", self.rooturi))
43        d.addCallback(lambda ign: self.do_cli("ls"))
44        def _check1(args):
45            (rc, out, err) = args
46            self.failUnlessReallyEqual(rc, 0)
47            self.assertEqual(len(err), 0, err)
48            expected = sorted(["0share", "1share", good_out])
49            self.assertEqual(sorted(out.splitlines()), expected)
50        d.addCallback(_check1)
51        d.addCallback(lambda ign: self.do_cli("ls", "missing"))
52        def _check2(args):
53            (rc, out, err) = args
54            self.failIfEqual(rc, 0)
55            self.assertEqual(err.strip(), "No such file or directory")
56            self.assertEqual(len(out), 0, out)
57        d.addCallback(_check2)
58        d.addCallback(lambda ign: self.do_cli("ls", "1share"))
59        def _check3(args):
60            (rc, out, err) = args
61            self.failIfEqual(rc, 0)
62            self.failUnlessIn("Error during GET: 410 Gone", err)
63            self.failUnlessIn("UnrecoverableFileError:", err)
64            self.failUnlessIn("could not be retrieved, because there were "
65                              "insufficient good shares.", err)
66            self.assertEqual(len(out), 0, out)
67        d.addCallback(_check3)
68        d.addCallback(lambda ign: self.do_cli("ls", "0share"))
69        d.addCallback(_check3)
70        def _check4(args):
71            (rc, out, err) = args
72            if good_out is None:
73                self.failUnlessReallyEqual(rc, 1)
74                self.failUnlessIn("files whose names could not be converted", err)
75                self.failUnlessIn(quote_output(u"g\u00F6\u00F6d"), err)
76                self.assertEqual(len(out), 0, out)
77            else:
78                # listing a file (as dir/filename) should have the edge metadata,
79                # including the filename
80                self.failUnlessReallyEqual(rc, 0)
81                self.failUnlessIn(good_out, out)
82                self.failIfIn("-r-- %d -" % len(small), out,
83                              "trailing hyphen means unknown date")
84
85        if good_arg is not None:
86            d.addCallback(lambda ign: self.do_cli("ls", "-l", good_arg))
87            d.addCallback(_check4)
88            # listing a file as $DIRCAP/filename should work just like dir/filename
89            d.addCallback(lambda ign: self.do_cli("ls", "-l", self.rooturi + "/" + good_arg))
90            d.addCallback(_check4)
91            # and similarly for $DIRCAP:./filename
92            d.addCallback(lambda ign: self.do_cli("ls", "-l", self.rooturi + ":./" + good_arg))
93            d.addCallback(_check4)
94
95        def _check5(args):
96            # listing a raw filecap should not explode, but it will have no
97            # metadata, just the size
98            (rc, out, err) = args
99            self.failUnlessReallyEqual(rc, 0)
100            self.assertEqual("-r-- %d -" % len(small), out.strip())
101        d.addCallback(lambda ign: self.do_cli("ls", "-l", self.goodcap))
102        d.addCallback(_check5)
103
104        # Now rename 'g\u00F6\u00F6d' to 'good' and repeat the tests that might have been skipped due
105        # to encoding problems.
106        d.addCallback(lambda ign: self.rootnode.move_child_to(u"g\u00F6\u00F6d", self.rootnode, u"good"))
107
108        d.addCallback(lambda ign: self.do_cli("ls"))
109        def _check1_ascii(args):
110            (rc,out,err) = args
111            self.failUnlessReallyEqual(rc, 0)
112            self.assertEqual(len(err), 0, err)
113            self.failUnlessReallyEqual(sorted(out.splitlines()), sorted(["0share", "1share", "good"]))
114        d.addCallback(_check1_ascii)
115        def _check4_ascii(args):
116            # listing a file (as dir/filename) should have the edge metadata,
117            # including the filename
118            (rc, out, err) = args
119            self.failUnlessReallyEqual(rc, 0)
120            self.failUnlessIn("good", out)
121            self.failIfIn("-r-- %d -" % len(small), out,
122                          "trailing hyphen means unknown date")
123
124        d.addCallback(lambda ign: self.do_cli("ls", "-l", "good"))
125        d.addCallback(_check4_ascii)
126        # listing a file as $DIRCAP/filename should work just like dir/filename
127        d.addCallback(lambda ign: self.do_cli("ls", "-l", self.rooturi + "/good"))
128        d.addCallback(_check4_ascii)
129        # and similarly for $DIRCAP:./filename
130        d.addCallback(lambda ign: self.do_cli("ls", "-l", self.rooturi + ":./good"))
131        d.addCallback(_check4_ascii)
132
133        unknown_immcap = b"imm.URI:unknown"
134        def _create_unknown(ign):
135            nm = c0.nodemaker
136            kids = {u"unknownchild-imm": (nm.create_from_cap(unknown_immcap), {})}
137            return self.rootnode.create_subdirectory(u"unknown", initial_children=kids,
138                                                     mutable=False)
139        d.addCallback(_create_unknown)
140        def _check6(args):
141            # listing a directory referencing an unknown object should print
142            # an extra message to stderr
143            (rc, out, err) = args
144            self.failUnlessReallyEqual(rc, 0)
145            self.failUnlessIn("?r-- ? - unknownchild-imm\n", out)
146            self.failUnlessIn("included unknown objects", err)
147        d.addCallback(lambda ign: self.do_cli("ls", "-l", "unknown"))
148        d.addCallback(_check6)
149        def _check7(args):
150            # listing an unknown cap directly should print an extra message
151            # to stderr (currently this only works if the URI starts with 'URI:'
152            # after any 'ro.' or 'imm.' prefix, otherwise it will be confused
153            # with an alias).
154            (rc, out, err) = args
155            self.failUnlessReallyEqual(rc, 0)
156            self.failUnlessIn("?r-- ? -\n", out)
157            self.failUnlessIn("included unknown objects", err)
158        d.addCallback(lambda ign: self.do_cli("ls", "-l", unknown_immcap))
159        d.addCallback(_check7)
160        return d
161
162    def test_list_without_alias(self):
163        # doing just 'tahoe ls' without specifying an alias or first
164        # doing 'tahoe create-alias tahoe' should fail gracefully.
165        self.basedir = "cli/List/list_without_alias"
166        self.set_up_grid(oneshare=True)
167        d = self.do_cli("ls")
168        def _check(args):
169            (rc, out, err) = args
170            self.failUnlessReallyEqual(rc, 1)
171            self.failUnlessIn("error:", err)
172            self.assertEqual(len(out), 0, out)
173        d.addCallback(_check)
174        return d
175
176    def test_list_with_nonexistent_alias(self):
177        # doing 'tahoe ls' while specifying an alias that doesn't already
178        # exist should fail with an informative error message
179        self.basedir = "cli/List/list_with_nonexistent_alias"
180        self.set_up_grid(oneshare=True)
181        d = self.do_cli("ls", "nonexistent:")
182        def _check(args):
183            (rc, out, err) = args
184            self.failUnlessReallyEqual(rc, 1)
185            self.failUnlessIn("error:", err)
186            self.failUnlessIn("nonexistent", err)
187            self.assertEqual(len(out), 0, out)
188        d.addCallback(_check)
189        return d
190
191    @defer.inlineCallbacks
192    def test_list_readonly(self):
193        self.basedir = "cli/List/list_readonly"
194        yield self.set_up_grid(oneshare=True)
195        c0 = self.g.clients[0]
196
197        root = yield c0.create_dirnode()
198        rooturi = root.get_uri()
199        rc, out, err = yield self.do_cli("add-alias", "tahoe", rooturi)
200        self.assertEqual(0, rc)
201        rc, out, err = yield self.do_cli("list-aliases", "--readonly-uri")
202        self.assertTrue('URI:DIR2-RO' in out)
203
204
205    def _create_directory_structure(self):
206        # Create a simple directory structure that we can use for MDMF,
207        # SDMF, and immutable testing.
208        assert self.g
209
210        client = self.g.clients[0]
211        # Create a dirnode
212        d = client.create_dirnode()
213        def _got_rootnode(n):
214            # Add a few nodes.
215            self._dircap = n.get_uri()
216            nm = n._nodemaker
217            # The uploaders may run at the same time, so we need two
218            # MutableData instances or they'll fight over offsets &c and
219            # break.
220            mutable_data = MutableData(b"data" * 100000)
221            mutable_data2 = MutableData(b"data" * 100000)
222            # Add both kinds of mutable node.
223            d1 = nm.create_mutable_file(mutable_data,
224                                        version=MDMF_VERSION)
225            d2 = nm.create_mutable_file(mutable_data2,
226                                        version=SDMF_VERSION)
227            # Add an immutable node. We do this through the directory,
228            # with add_file.
229            immutable_data = upload.Data(b"immutable data" * 100000,
230                                         convergence=b"")
231            d3 = n.add_file(u"immutable", immutable_data)
232            ds = [d1, d2, d3]
233            dl = defer.DeferredList(ds)
234            def _made_files(args):
235                (r1, r2, r3) = args
236                self.failUnless(r1[0])
237                self.failUnless(r2[0])
238                self.failUnless(r3[0])
239
240                # r1, r2, and r3 contain nodes.
241                mdmf_node = r1[1]
242                sdmf_node = r2[1]
243                imm_node = r3[1]
244
245                self._mdmf_uri = mdmf_node.get_uri()
246                self._mdmf_readonly_uri = mdmf_node.get_readonly_uri()
247                self._sdmf_uri = mdmf_node.get_uri()
248                self._sdmf_readonly_uri = sdmf_node.get_readonly_uri()
249                self._imm_uri = imm_node.get_uri()
250
251                d1 = n.set_node(u"mdmf", mdmf_node)
252                d2 = n.set_node(u"sdmf", sdmf_node)
253                return defer.DeferredList([d1, d2])
254            # We can now list the directory by listing self._dircap.
255            dl.addCallback(_made_files)
256            return dl
257        d.addCallback(_got_rootnode)
258        return d
259
260    def test_list_mdmf(self):
261        # 'tahoe ls' should include MDMF files.
262        self.basedir = "cli/List/list_mdmf"
263        self.set_up_grid(oneshare=True)
264        d = self._create_directory_structure()
265        d.addCallback(lambda ignored:
266            self.do_cli("ls", self._dircap))
267        def _got_ls(args):
268            (rc, out, err) = args
269            self.failUnlessEqual(rc, 0)
270            self.failUnlessEqual(err, "")
271            self.failUnlessIn("immutable", out)
272            self.failUnlessIn("mdmf", out)
273            self.failUnlessIn("sdmf", out)
274        d.addCallback(_got_ls)
275        return d
276
277    def test_list_mdmf_json(self):
278        # 'tahoe ls' should include MDMF caps when invoked with MDMF
279        # caps.
280        self.basedir = "cli/List/list_mdmf_json"
281        self.set_up_grid(oneshare=True)
282        d = self._create_directory_structure()
283        d.addCallback(lambda ignored:
284            self.do_cli("ls", "--json", self._dircap))
285        def _got_json(args):
286            (rc, out, err) = args
287            self.failUnlessEqual(rc, 0)
288            self.assertEqual(len(err), 0, err)
289            self.failUnlessIn(str(self._mdmf_uri, "ascii"), out)
290            self.failUnlessIn(str(self._mdmf_readonly_uri, "ascii"), out)
291            self.failUnlessIn(str(self._sdmf_uri, "ascii"), out)
292            self.failUnlessIn(str(self._sdmf_readonly_uri, "ascii"), out)
293            self.failUnlessIn(str(self._imm_uri, "ascii"), out)
294            self.failUnlessIn('"format": "SDMF"', out)
295            self.failUnlessIn('"format": "MDMF"', out)
296        d.addCallback(_got_json)
297        return d
Note: See TracBrowser for help on using the repository browser.