Merge pull request #831 from tahoe-lafs/3438.test_introweb

Split `_test_introweb` into a separate suite

Fixes: ticket:3438
This commit is contained in:
Jean-Paul Calderone 2020-09-27 19:14:24 -04:00 committed by GitHub
commit 60f531e970
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 199 additions and 82 deletions

0
newsfragments/3438.minor Normal file
View File

View File

@ -159,9 +159,12 @@ class FakeCanary(object):
if self.ignore:
return
del self.disconnectors[marker]
def getRemoteTubID(self):
return None
def getPeer(self):
return "<fake>"
class LoggingServiceParent(service.MultiService):
def log(self, *args, **kwargs):
return log.msg(*args, **kwargs)

View File

@ -11,7 +11,6 @@ from twisted.internet import defer
from twisted.internet.defer import inlineCallbacks
from twisted.application import service
import allmydata
from allmydata import client, uri
from allmydata.introducer.server import create_introducer
from allmydata.storage.mutable import MutableShareFile
@ -1629,7 +1628,6 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
for c in self.clients:
c.encoding_params['happy'] = 1
d.addCallback(_new_happy_semantics)
d.addCallback(self._test_introweb)
d.addCallback(self.log, "starting publish")
d.addCallback(self._do_publish1)
d.addCallback(self._test_runner)
@ -1668,58 +1666,6 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
d.addCallback(self._test_checker)
return d
def _test_introweb(self, res):
d = do_http("get", self.introweb_url)
def _check(res):
try:
self.failUnless("%s: %s" % (allmydata.__appname__, allmydata.__version__) in res)
verstr = str(allmydata.__version__)
# The Python "rational version numbering" convention
# disallows "-r$REV" but allows ".post$REV"
# instead. Eventually we'll probably move to
# that. When we do, this test won't go red:
ix = verstr.rfind('-r')
if ix != -1:
altverstr = verstr[:ix] + '.post' + verstr[ix+2:]
else:
ix = verstr.rfind('.post')
if ix != -1:
altverstr = verstr[:ix] + '-r' + verstr[ix+5:]
else:
altverstr = verstr
appverstr = "%s: %s" % (allmydata.__appname__, verstr)
newappverstr = "%s: %s" % (allmydata.__appname__, altverstr)
self.failUnless((appverstr in res) or (newappverstr in res), (appverstr, newappverstr, res))
self.failUnless("Announcement Summary: storage: 5" in res)
self.failUnless("Subscription Summary: storage: 5" in res)
self.failUnless("tahoe.css" in res)
except unittest.FailTest:
print()
print("GET %s output was:" % self.introweb_url)
print(res)
raise
d.addCallback(_check)
# make sure it serves the CSS too
d.addCallback(lambda res: do_http("get", self.introweb_url+"tahoe.css"))
d.addCallback(lambda res: do_http("get", self.introweb_url + "?t=json"))
def _check_json(res):
data = json.loads(res)
try:
self.failUnlessEqual(data["subscription_summary"],
{"storage": 5})
self.failUnlessEqual(data["announcement_summary"],
{"storage": 5})
except unittest.FailTest:
print()
print("GET %s?t=json output was:" % self.introweb_url)
print(res)
raise
d.addCallback(_check_json)
return d
def _do_publish1(self, res):
ut = upload.Data(self.data, convergence=None)
c0 = self.clients[0]

View File

@ -1,57 +1,225 @@
from bs4 import BeautifulSoup
import json
from os.path import join
from bs4 import BeautifulSoup
from twisted.trial import unittest
from twisted.internet import reactor
from foolscap.api import fireEventually, flushEventualQueue
from twisted.internet import defer
from allmydata.introducer import create_introducer
from foolscap.api import (
fireEventually,
flushEventualQueue,
Tub,
)
import allmydata
from allmydata.introducer import (
create_introducer,
)
from allmydata.introducer.server import (
_IntroducerNode,
)
from allmydata.web.introweb import (
IntroducerRoot,
)
from allmydata import node
from .common import (
assert_soup_has_favicon,
assert_soup_has_text,
assert_soup_has_tag_with_attributes,
)
from ..common import (
SameProcessStreamEndpointAssigner,
)
from ..common_web import do_http
from ..common_py3 import (
FakeCanary,
)
from ..common_web import (
do_http,
render,
)
@defer.inlineCallbacks
def create_introducer_webish(reactor, port_assigner, basedir):
"""
Create and start an introducer node and return it and its ``WebishServer``
service.
:param reactor: The reactor to use to allow the introducer node to use to
listen for connections.
:param SameProcessStreamEndpointAssigner port_assigner: The assigner to
use to assign a listening port for the introducer node.
:param bytes basedir: A non-existant path where the introducer node will
be created.
:return Deferred[(_IntroducerNode, WebishServer)]: A Deferred that fires
with the node and its webish service.
"""
node.create_node_dir(basedir, "testing")
_, port_endpoint = port_assigner.assign(reactor)
with open(join(basedir, "tahoe.cfg"), "w") as f:
f.write(
"[node]\n"
"tub.location = 127.0.0.1:1\n" +
"web.port = {}\n".format(port_endpoint)
)
intro_node = yield create_introducer(basedir)
ws = intro_node.getServiceNamed("webish")
yield fireEventually(None)
intro_node.startService()
defer.returnValue((intro_node, ws))
class IntroducerWeb(unittest.TestCase):
"""
Tests for web-facing functionality of an introducer node.
"""
def setUp(self):
self.node = None
self.port_assigner = SameProcessStreamEndpointAssigner()
self.port_assigner.setUp()
self.addCleanup(self.port_assigner.tearDown)
def tearDown(self):
d = defer.succeed(None)
if self.node:
d.addCallback(lambda ign: self.node.stopService())
d.addCallback(flushEventualQueue)
return d
# Anything using Foolscap leaves some timer trash in the reactor that
# we have to arrange to have cleaned up.
self.addCleanup(lambda: flushEventualQueue(None))
@defer.inlineCallbacks
def test_welcome(self):
basedir = self.mktemp()
node.create_node_dir(basedir, "testing")
_, port_endpoint = self.port_assigner.assign(reactor)
with open(join(basedir, "tahoe.cfg"), "w") as f:
f.write(
"[node]\n"
"tub.location = 127.0.0.1:1\n" +
"web.port = {}\n".format(port_endpoint)
)
node, ws = yield create_introducer_webish(
reactor,
self.port_assigner,
self.mktemp(),
)
self.addCleanup(node.stopService)
self.node = yield create_introducer(basedir)
self.ws = self.node.getServiceNamed("webish")
yield fireEventually(None)
self.node.startService()
url = "http://localhost:%d/" % self.ws.getPortnum()
url = "http://localhost:%d/" % (ws.getPortnum(),)
res = yield do_http("get", url)
soup = BeautifulSoup(res, 'html5lib')
assert_soup_has_text(self, soup, u'Welcome to the Tahoe-LAFS Introducer')
assert_soup_has_favicon(self, soup)
assert_soup_has_text(self, soup, u'Page rendered at')
assert_soup_has_text(self, soup, u'Tahoe-LAFS code imported from:')
@defer.inlineCallbacks
def test_basic_information(self):
"""
The introducer web page includes the software version and several other
simple pieces of information.
"""
node, ws = yield create_introducer_webish(
reactor,
self.port_assigner,
self.mktemp(),
)
self.addCleanup(node.stopService)
url = "http://localhost:%d/" % (ws.getPortnum(),)
res = yield do_http("get", url)
soup = BeautifulSoup(res, 'html5lib')
assert_soup_has_text(
self,
soup,
u"%s: %s" % (allmydata.__appname__, allmydata.__version__),
)
assert_soup_has_text(self, soup, u"no peers!")
assert_soup_has_text(self, soup, u"subscribers!")
assert_soup_has_tag_with_attributes(
self,
soup,
"link",
{"href": "/tahoe.css"},
)
@defer.inlineCallbacks
def test_tahoe_css(self):
"""
The introducer serves the css.
"""
node, ws = yield create_introducer_webish(
reactor,
self.port_assigner,
self.mktemp(),
)
self.addCleanup(node.stopService)
url = "http://localhost:%d/tahoe.css" % (ws.getPortnum(),)
# Just don't return an error. If it does, do_http will raise
# something.
yield do_http("get", url)
@defer.inlineCallbacks
def test_json_front_page(self):
"""
The front page can be served as json.
"""
node, ws = yield create_introducer_webish(
reactor,
self.port_assigner,
self.mktemp(),
)
self.addCleanup(node.stopService)
url = "http://localhost:%d/?t=json" % (ws.getPortnum(),)
res = yield do_http("get", url)
data = json.loads(res)
self.assertEqual(data["subscription_summary"], {})
self.assertEqual(data["announcement_summary"], {})
class IntroducerRootTests(unittest.TestCase):
"""
Tests for ``IntroducerRoot``.
"""
def test_json(self):
"""
The JSON response includes totals for the number of subscriptions and
announcements of each service type.
"""
config = node.config_from_string(self.mktemp(), "", "")
config.get_private_path = lambda ignored: self.mktemp()
main_tub = Tub()
main_tub.listenOn(b"tcp:0")
main_tub.setLocation(b"tcp:127.0.0.1:1")
introducer_node = _IntroducerNode(config, main_tub, None, None, None)
introducer_service = introducer_node.getServiceNamed("introducer")
for n in range(2):
introducer_service.add_subscriber(
FakeCanary(),
"arbitrary",
{"info": "info"},
)
# It would be nice to use the publish method but then we have to
# generate a correctly signed message which I don't feel like doing.
ann_t = ("msg", "sig", "key")
ann = {"service-name": "arbitrary"}
introducer_service._announcements[("arbitrary", "key")] = (
ann_t,
FakeCanary(),
ann,
0,
)
resource = IntroducerRoot(introducer_node)
response = json.loads(
self.successResultOf(
render(resource, {"t": [b"json"]}),
),
)
self.assertEqual(
response,
{
u"subscription_summary": {"arbitrary": 2},
u"announcement_summary": {"arbitrary": 1},
},
)