]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blobdiff - src/allmydata/test/test_web.py
Fix an error-reporting problem in test_welcome (this does not fix the underlying...
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / test / test_web.py
index 077ea0286d9dae59f5f5007d40c7d10ee4d5c04b..18514a3a57b012bbebf4cbe3d35b9a9e9d3571cf 100644 (file)
@@ -50,7 +50,7 @@ unknown_rocap = u"ro.lafs://readonly_from_the_future_ro_\u263A".encode('utf-8')
 unknown_immcap = u"imm.lafs://immutable_from_the_future_imm_\u263A".encode('utf-8')
 
 FAVICON_MARKUP = '<link href="/icon.png" rel="shortcut icon" />'
-
+DIR_HTML_TAG = '<html lang="en">'
 
 class FakeStatsProvider:
     def get_stats(self):
@@ -172,25 +172,34 @@ class FakeHistory:
         return []
 
 class FakeDisplayableServer(StubServer):
-    def __init__(self, serverid, nickname):
+    def __init__(self, serverid, nickname, connected,
+                 last_connect_time, last_loss_time, last_rx_time):
         StubServer.__init__(self, serverid)
         self.announcement = {"my-version": "allmydata-tahoe-fake",
                              "service-name": "storage",
                              "nickname": nickname}
+        self.connected = connected
+        self.last_loss_time = last_loss_time
+        self.last_rx_time = last_rx_time
+        self.last_connect_time = last_connect_time
     def is_connected(self):
-        return True
+        return self.connected
     def get_permutation_seed(self):
         return ""
     def get_remote_host(self):
         return ""
     def get_last_loss_time(self):
-        return None
-    def get_announcement_time(self):
-        return None
+        return self.last_loss_time
+    def get_last_received_data_time(self):
+        return self.last_rx_time
+    def get_last_connect_time(self):
+        return self.last_connect_time
     def get_announcement(self):
         return self.announcement
     def get_nickname(self):
         return self.announcement["nickname"]
+    def get_available_space(self):
+        return 123456
 
 class FakeBucketCounter(object):
     def get_state(self):
@@ -240,7 +249,13 @@ class FakeClient(Client):
         self.storage_broker = StorageFarmBroker(None, permute_peers=True)
         # fake knowledge of another server
         self.storage_broker.test_add_server("other_nodeid",
-                                            FakeDisplayableServer("other_nodeid", u"other_nickname \u263B"))
+            FakeDisplayableServer(
+                serverid="other_nodeid", nickname=u"other_nickname \u263B", connected = True,
+                last_connect_time = 10, last_loss_time = 20, last_rx_time = 30))
+        self.storage_broker.test_add_server("disconnected_nodeid",
+            FakeDisplayableServer(
+                serverid="other_nodeid", nickname=u"disconnected_nickname \u263B", connected = False,
+                last_connect_time = 15, last_loss_time = 25, last_rx_time = 35))
         self.introducer_client = None
         self.history = FakeHistory()
         self.uploader = FakeUploader()
@@ -266,14 +281,16 @@ class FakeClient(Client):
 
     MUTABLE_SIZELIMIT = FakeMutableFileNode.MUTABLE_SIZELIMIT
 
-class WebMixin(object):
+class WebMixin(testutil.TimezoneMixin):
     def setUp(self):
+        self.setTimezone('UTC-13:00')
         self.s = FakeClient()
         self.s.startService()
         self.staticdir = self.mktemp()
         self.clock = Clock()
+        self.fakeTime = 86460 # 1d 0h 1m 0s
         self.ws = webish.WebishServer(self.s, "0", staticdir=self.staticdir,
-                                      clock=self.clock)
+                                      clock=self.clock, now_fn=lambda:self.fakeTime)
         self.ws.setServiceParent(self.s)
         self.webish_port = self.ws.getPortnum()
         self.webish_url = self.ws.getURL()
@@ -599,7 +616,6 @@ class WebMixin(object):
             self.fail("%s was supposed to Error(302), not get '%s'" %
                       (which, res))
 
-
 class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixin, unittest.TestCase):
     def test_create(self):
         pass
@@ -611,13 +627,20 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
             self.failUnlessIn(FAVICON_MARKUP, res)
             self.failUnlessIn('<a href="status">Recent and Active Operations</a>', res)
             self.failUnlessIn('<a href="statistics">Operational Statistics</a>', res)
-            self.failUnlessIn('<input type="hidden" name="t" value="report-incident" />', res)
+            self.failUnless(re.search('<input (type="hidden" |name="t" |value="report-incident" ){3}/>',res), res)
             self.failUnlessIn('Page rendered at', res)
             self.failUnlessIn('Tahoe-LAFS code imported from:', res)
             res_u = res.decode('utf-8')
             self.failUnlessIn(u'<td>fake_nickname \u263A</td>', res_u)
             self.failUnlessIn(u'<div class="nickname">other_nickname \u263B</div>', res_u)
+            self.failUnlessIn(u'Connected to <span>1</span>\n              of <span>2</span> known storage servers', res_u)
+            self.failUnless(re.search(u'<div class="status-indicator"><img (src="img/connected-yes.png" |alt="Connected" ){2}/></div>\n                <a( class="timestamp"| title="1970-01-01 13:00:10"){2}>1d\u00A00h\u00A00m\u00A050s</a>', res_u), repr(res_u))
+            self.failUnless(re.search(u'<div class="status-indicator"><img (src="img/connected-no.png" |alt="Disconnected" ){2}/></div>\n                <a( class="timestamp"| title="1970-01-01 13:00:25"){2}>1d\u00A00h\u00A00m\u00A035s</a>', res_u), repr(res_u))
+            self.failUnless(re.search(u'<td class="service-last-received-data"><a( class="timestamp"| title="1970-01-01 13:00:30"){2}>1d\u00A00h\u00A00m\u00A030s</a></td>', res_u), repr(res_u))
+            self.failUnless(re.search(u'<td class="service-last-received-data"><a( class="timestamp"| title="1970-01-01 13:00:35"){2}>1d\u00A00h\u00A00m\u00A025s</a></td>', res_u), repr(res_u))
             self.failUnlessIn(u'\u00A9 <a href="https://tahoe-lafs.org/">Tahoe-LAFS Software Foundation', res_u)
+            self.failUnlessIn('<td><h3>Available</h3></td>', res)
+            self.failUnlessIn('123.5kB', res)
 
             self.s.basedir = 'web/test_welcome'
             fileutil.make_dirs("web/test_welcome")
@@ -645,7 +668,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
             html = res.replace('\n', ' ')
             self.failUnlessIn('<div class="furl">pb://someIntroducer/[censored]</div>', html)
             self.failIfIn('pb://someIntroducer/secret', html)
-            self.failUnless(re.search('<div class="status-indicator connected-no"></div>[ ]*<div>Introducer not connected</div>', html), res)
+            self.failUnless(re.search('<img (alt="Disconnected" |src="img/connected-no.png" ){2}/>', html), res)
         d.addCallback(_check_introducer_not_connected_unguessable)
 
         # introducer connected, unguessable furl
@@ -658,7 +681,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
             html = res.replace('\n', ' ')
             self.failUnlessIn('<div class="furl">pb://someIntroducer/[censored]</div>', html)
             self.failIfIn('pb://someIntroducer/secret', html)
-            self.failUnless(re.search('<div class="status-indicator connected-yes"></div>[ ]*<div>Introducer</div>', html), res)
+            self.failUnless(re.search('<img (src="img/connected-yes.png" |alt="Connected" ){2}/>', html), res)
         d.addCallback(_check_introducer_connected_unguessable)
 
         # introducer connected, guessable furl
@@ -670,7 +693,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
         def _check_introducer_connected_guessable(res):
             html = res.replace('\n', ' ')
             self.failUnlessIn('<div class="furl">pb://someIntroducer/introducer</div>', html)
-            self.failUnless(re.search('<div class="status-indicator connected-yes"></div>[ ]*<div>Introducer</div>', html), res)
+            self.failUnless(re.search('<img (src="img/connected-yes.png" |alt="Connected" ){2}/>', html), res)
         d.addCallback(_check_introducer_connected_guessable)
         return d
 
@@ -684,7 +707,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
         d.addCallback(_set_no_helper)
         def _check_no_helper(res):
             html = res.replace('\n', ' ')
-            self.failUnless(re.search('<div class="status-indicator connected-not-configured"></div>[ ]*<div>Helper</div>', html), res)
+            self.failUnless(re.search('<img (src="img/connected-not-configured.png" |alt="Not Configured" ){2}/>', html), res)
         d.addCallback(_check_no_helper)
 
         # enable helper, not connected
@@ -697,7 +720,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
             html = res.replace('\n', ' ')
             self.failUnlessIn('<div class="furl">pb://someHelper/[censored]</div>', html)
             self.failIfIn('pb://someHelper/secret', html)
-            self.failUnless(re.search('<div class="status-indicator connected-no"></div>[ ]*<div>Helper not connected</div>', html), res)
+            self.failUnless(re.search('<img (src="img/connected-no.png" |alt="Disconnected" ){2}/>', html), res)
         d.addCallback(_check_helper_not_connected)
 
         # enable helper, connected
@@ -710,7 +733,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
             html = res.replace('\n', ' ')
             self.failUnlessIn('<div class="furl">pb://someHelper/[censored]</div>', html)
             self.failIfIn('pb://someHelper/secret', html)
-            self.failUnless(re.search('<div class="status-indicator connected-yes"></div>[ ]*<div>Helper</div>', html), res)
+            self.failUnless(re.search('<img (src="img/connected-yes.png" |alt="Connected" ){2}/>', html), res)
         d.addCallback(_check_helper_connected)
         return d
 
@@ -1505,7 +1528,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
     def _check_upload_and_mkdir_forms(self, html):
         # We should have a form to create a file, with radio buttons that allow
         # the user to toggle whether it is a CHK/LIT (default), SDMF, or MDMF file.
-        self.failUnlessIn('name="t" value="upload"', html)
+        self.failUnless(re.search('<input (name="t" |value="upload" |type="hidden" ){3}/>', html), html)
         self.failUnless(re.search('<input [^/]*id="upload-chk"', html), html)
         self.failUnless(re.search('<input [^/]*id="upload-sdmf"', html), html)
         self.failUnless(re.search('<input [^/]*id="upload-mdmf"', html), html)
@@ -1513,7 +1536,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
         # We should also have the ability to create a mutable directory, with
         # radio buttons that allow the user to toggle whether it is an SDMF (default)
         # or MDMF directory.
-        self.failUnlessIn('name="t" value="mkdir"', html)
+        self.failUnless(re.search('<input (name="t" |value="mkdir" |type="hidden" ){3}/>', html), html)
         self.failUnless(re.search('<input [^/]*id="mkdir-sdmf"', html), html)
         self.failUnless(re.search('<input [^/]*id="mkdir-mdmf"', html), html)
 
@@ -1522,7 +1545,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
     def test_GET_DIRECTORY_html(self):
         d = self.GET(self.public_url + "/foo", followRedirect=True)
         def _check(html):
-            self.failUnlessIn('<div class="toolbar-item"><a href="../../..">Return to Welcome page</a></div>', html)
+            self.failUnlessIn('<li class="toolbar-item"><a href="../../..">Return to Welcome page</a></li>', html)
             self._check_upload_and_mkdir_forms(html)
             self.failUnlessIn("quux", html)
         d.addCallback(_check)
@@ -1616,7 +1639,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
         d.addCallback(lambda res: self.GET(self.public_url + "/foo/empty/"))
         def _check4(res):
             self.failUnlessIn("directory is empty", res)
-            MKDIR_BUTTON_RE=re.compile('<input type="hidden" name="t" value="mkdir" />.*<legend class="freeform-form-label">Create a new directory in this directory</legend>.*<input type="submit" value="Create" />', re.I)
+            MKDIR_BUTTON_RE=re.compile('<input (type="hidden" |name="t" |value="mkdir" ){3}/>.*<legend class="freeform-form-label">Create a new directory in this directory</legend>.*<input (type="submit" |class="btn" |value="Create" ){3}/>', re.I)
             self.failUnless(MKDIR_BUTTON_RE.search(res), res)
         d.addCallback(_check4)
 
@@ -3254,10 +3277,10 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
         d = self.GET("/")
         def _after_get_welcome_page(res):
             MKDIR_BUTTON_RE = re.compile(
-                '<form action="([^"]*)" method="post".*'
-                '<input type="hidden" name="t" value="([^"]*)" />[ ]*'
-                '<input type="hidden" name="([^"]*)" value="([^"]*)" />[ ]*'
-                '<input type="submit" class="btn" value="Create a directory[^"]*" />')
+                '<form(?: action="([^"]*)"| method="post"| enctype="multipart/form-data"){3}>.*'
+                '<input (?:type="hidden" |name="t" |value="([^"]*?)" ){3}/>[ ]*'
+                '<input (?:type="hidden" |name="([^"]*)" |value="([^"]*)" ){3}/>[ ]*'
+                '<input (type="submit" |class="btn" |value="Create a directory[^"]*" ){3}/>')
             html = res.replace('\n', ' ')
             mo = MKDIR_BUTTON_RE.search(html)
             self.failUnless(mo, html)
@@ -3870,8 +3893,8 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
         d = self.GET(self.public_url + "/foo?t=rename-form&name=bar.txt",
                      followRedirect=True)
         def _check(res):
-            self.failUnlessIn('name="when_done" value="."', res)
-            self.failUnless(re.search(r'name="from_name" value="bar\.txt"', res))
+            self.failUnless(re.search('<input (name="when_done" |value="." |type="hidden" ){3}/>', res), res)
+            self.failUnless(re.search(r'<input (readonly="true" |type="text" |name="from_name" |value="bar\.txt" ){4}/>', res), res)
             self.failUnlessIn(FAVICON_MARKUP, res)
         d.addCallback(_check)
         return d
@@ -4411,7 +4434,11 @@ class IntroducerWeb(unittest.TestCase):
     def test_welcome(self):
         basedir = "web.IntroducerWeb.test_welcome"
         os.mkdir(basedir)
-        fileutil.write(os.path.join(basedir, "tahoe.cfg"), "[node]\nweb.port = tcp:0\n")
+        cfg = "\n".join(["[node]",
+                         "tub.location = 127.0.0.1:1",
+                         "web.port = tcp:0",
+                         ]) + "\n"
+        fileutil.write(os.path.join(basedir, "tahoe.cfg"), cfg)
         self.node = IntroducerNode(basedir)
         self.ws = self.node.getServiceNamed("webish")
 
@@ -5590,7 +5617,7 @@ class Grid(GridTestMixin, WebErrorMixin, ShouldFailMixin, testutil.ReallyEqualMi
 
         d.addCallback(lambda ignored: self.GET(self.fileurls["dir-0share"]))
         def _check_0shares_dir_html(body):
-            self.failUnlessIn("<html>", body)
+            self.failUnlessIn(DIR_HTML_TAG, body)
             # we should see the regular page, but without the child table or
             # the dirops forms
             body = " ".join(body.strip().split())
@@ -5613,7 +5640,7 @@ class Grid(GridTestMixin, WebErrorMixin, ShouldFailMixin, testutil.ReallyEqualMi
             # and some-shares like we did for immutable files (since there
             # are different sorts of advice to offer in each case). For now,
             # they present the same way.
-            self.failUnlessIn("<html>", body)
+            self.failUnlessIn(DIR_HTML_TAG, body)
             body = " ".join(body.strip().split())
             self.failUnlessIn('href="?t=info">More info on this directory',
                               body)
@@ -5740,7 +5767,7 @@ class Grid(GridTestMixin, WebErrorMixin, ShouldFailMixin, testutil.ReallyEqualMi
         d.addCallback(_stash_dir)
         d.addCallback(lambda ign: self.GET(self.dir_url, followRedirect=True))
         def _check_dir_html(body):
-            self.failUnlessIn("<html>", body)
+            self.failUnlessIn(DIR_HTML_TAG, body)
             self.failUnlessIn("blacklisted.txt</a>", body)
         d.addCallback(_check_dir_html)
         d.addCallback(lambda ign: self.GET(self.url))
@@ -5764,7 +5791,7 @@ class Grid(GridTestMixin, WebErrorMixin, ShouldFailMixin, testutil.ReallyEqualMi
         # We should still be able to list the parent directory, in HTML...
         d.addCallback(lambda ign: self.GET(self.dir_url, followRedirect=True))
         def _check_dir_html2(body):
-            self.failUnlessIn("<html>", body)
+            self.failUnlessIn(DIR_HTML_TAG, body)
             self.failUnlessIn("blacklisted.txt</strike>", body)
         d.addCallback(_check_dir_html2)
 
@@ -5815,7 +5842,7 @@ class Grid(GridTestMixin, WebErrorMixin, ShouldFailMixin, testutil.ReallyEqualMi
             self.child_url = "uri/"+dn.get_readonly_uri()+"/child"
         d.addCallback(_get_dircap)
         d.addCallback(lambda ign: self.GET(self.dir_url_base, followRedirect=True))
-        d.addCallback(lambda body: self.failUnlessIn("<html>", body))
+        d.addCallback(lambda body: self.failUnlessIn(DIR_HTML_TAG, body))
         d.addCallback(lambda ign: self.GET(self.dir_url_json1))
         d.addCallback(lambda res: simplejson.loads(res))  # just check it decodes
         d.addCallback(lambda ign: self.GET(self.dir_url_json2))