Started to add real unit tests!

We can now verify that Santiago 'queues' and sends requested messages.
This commit is contained in:
Nick Daly 2012-03-12 22:52:26 -05:00
parent f2dd818d3d
commit e2ef78abbd
3 changed files with 137 additions and 37 deletions

View File

@ -40,7 +40,7 @@ class SantiagoHttpListener(santiago.SantiagoListener):
<html><head><title>Use it right.</title></head><body>
<p>Nice try, now try again with a request like:</p>
<p>http://localhost:8080/santiago/(requester)/(server)/(service)</p>
<p>http://localhost:8080/serve/(requester)/(server)/(service)</p>
<dl>
<dt>requster</dt><dd>james, ian</dd>
@ -49,17 +49,17 @@ class SantiagoHttpListener(santiago.SantiagoListener):
</dl>
<p>This'll get you good results:</p>
<code><a href="http://localhost:8080/santiago/james/nick/wiki">
http://localhost:8080/santiago/james/nick/wiki</a></code>
<code><a href="http://localhost:8080/serve/james/wiki/nick">
http://localhost:8080/serve/james/wiki/nick</a></code>
<p>See the <code>serving_to</code>, <code>serving_what</code>, and
<code>me</code> variables.</p>
</body></html>"""
def __init__(self, instance, port=8080):
super(SantiagoHttpListener, self).__init__(instance)
self.socket_port = port
def __init__(self, instance, location="localhost:8080"):
super(SantiagoHttpListener, self).__init__(instance, location)
self.socket_port = location.rpartition(":")[2]
@cherrypy.expose
@cherrypy.tools.json_out()

View File

@ -66,7 +66,7 @@ class Santiago(object):
Each Santiago keeps track of the services it hosts, and other servers'
Santiago services. A Santiago has no idea of and is not responsible for
anybody else's services.
"""
self.instance = instance
self.hosting = self.load_dict("hosting")
@ -95,7 +95,7 @@ class Santiago(object):
def am_i(self, server):
"""Hello? Is it me you're looking for?"""
return self.instance == server
# Server-related tags
@ -106,7 +106,7 @@ class Santiago(object):
post::
location in cfg.santiago.hosting[service]
location in self.hosting[service]
"""
self.hosting[service].append(location)
@ -116,7 +116,7 @@ class Santiago(object):
post::
service in cfg.santiago.keys[key]
service in self.keys[key]
"""
self.keys[key].append(service)
@ -129,7 +129,7 @@ class Santiago(object):
post::
forall(locations, lambda x: x in cfg.santiago.servers[service][key])
forall(locations, lambda x: x in self.servers[service][key])
"""
self.servers[service][key] += locations
@ -139,7 +139,7 @@ class Santiago(object):
def add_listener(self, listener):
"""Registers a protocol-specific listener."""
self.listeners.append(listener)
def add_sender(self, sender):
@ -149,18 +149,19 @@ class Santiago(object):
# processing related tags
# -----------------------
def serve(self, key, service, server, hops, santiagi):
def serve(self, key, service, server, hops, santiagi, listener):
"""Provide a requested service to a client."""
if santiagi is not None:
self.learn_service("santiago", key, santiagi)
if not self.am_i(server):
return self.proxy(key, service, server, hops=hops)
self.proxy(key, service, server, hops=hops)
if service in self.keys[key]:
return self.hosting[service]
# TODO pick the senders more intelligently.
self.senders[0].ack(key, self.hosting[service], listener)
def proxy(self, key, service, server, hops=3):
"""Passes a Santiago request off to another known host.
@ -183,11 +184,12 @@ class Santiago(object):
class SantiagoListener(object):
"""Listens for requests on the santiago port."""
def __init__(self, santiago):
def __init__(self, santiago, location):
self.santiago = santiago
self.location = location
def serve(self, key, service, server, hops, santiagi):
return self.santiago.serve(key, service, server, hops, santiagi)
return self.santiago.serve(key, service, server, hops, santiagi, self.location)
class SantiagoSender(object):
@ -195,6 +197,14 @@ class SantiagoSender(object):
def __init__(self, santiago):
self.santiago = santiago
self.messages = list()
def send(self):
"""Sends all messages on the queue."""
answer = self.messages
self.messages = list()
return answer
def request(self, destination, resource):
"""Sends a request for a resource to a known Santiago.
@ -216,7 +226,7 @@ class SantiagoSender(object):
# TODO my request is signed with my GPG key, recipient encrypted.
"""
pass # TODO: do.
pass # TODO: queue a request message.
def nak(self):
"""Denies a requested resource to a Santiago.
@ -227,7 +237,7 @@ class SantiagoSender(object):
"""
pass
def ack(self):
def ack(self, key, location, listener=None):
"""A successful reply to a Santiago request.
The response must include:
@ -239,7 +249,10 @@ class SantiagoSender(object):
- The Santiago listener that received and accepted the request.
"""
pass
self.messages.append({
"to": key,
"location": location,
"reply-to": listener,})
def end(self):
"""Sent by the original requester, when it receives the server's
@ -252,7 +265,7 @@ class SantiagoSender(object):
def proxy(self, key, service, server, hops):
"""Sends the request to another server."""
# TODO pull this off, another day.
return ("%(key)s is requesting the %(service)s from %(server)s. " +
self.santiago.instance + " is not %(server)s. " +

View File

@ -14,30 +14,117 @@
"""
import unittest
from protocols.http import SantiagoHttpSender, SantiagoHttpListener
import santiago
# TODO import some http client and validate my responses!
import sys
class TestInitialRequest(unittest.TestCase):
class SantiagoTest(unittest.TestCase):
"""The base class for tests."""
def setUp(self):
self.santiago = santiago.Santiago("0x1")
if sys.version_info < (2, 7):
"""Add a poor man's forward compatibility."""
class ContainsError(AssertionError):
pass
def assertIn(self, a, b):
if not a in b:
raise self.ContainsError("%s not in %s" % (a, b))
class TestDataModel(SantiagoTest):
"""Test adding and removing services and data."""
def test_add_listener(self):
"""Verify that we can add a listener."""
http_listener = SantiagoHttpListener(self.santiago, "localhost:8080")
self.santiago.add_listener(http_listener)
self.assertIn(http_listener, self.santiago.listeners)
def test_add_sender(self):
"""Verify that we can add a sender."""
http_sender = SantiagoHttpSender(self.santiago)
self.santiago.add_sender(http_sender)
self.assertIn(http_sender, self.santiago.senders)
def test_provide_at_location(self):
"""Verify that we can provide a service somewhere."""
service, location = ("something", "there")
self.santiago.provide_at_location(service, location)
self.assertIn(location, self.santiago.hosting[service])
def test_provide_for_key(self):
"""Verify we can provide a specific service to someone."""
service, key = ("something", "0x1")
self.santiago.provide_for_key(service, key)
self.assertIn(service, self.santiago.keys[key])
class TestServing(SantiagoTest):
"""TODO: tests for:
(normal serving + proxying) * (learning santiagi + not learning)
"""
def setUp(self):
super(TestServing, self).setUp()
self.santiago.provide_at_location("wiki", "192.168.0.13")
self.santiago.provide_for_key("wiki", "0x2")
self.listener = santiago.SantiagoListener(self.santiago, "localhost:8080")
self.sender = santiago.SantiagoSender(self.santiago)
self.santiago.add_listener(self.listener)
self.santiago.add_sender(self.sender)
def test_successful_serve(self):
"""Make sure we get an expected, successful serving message back."""
self.listener.serve("0x2", "wiki", "0x1", 0, None)
expected = [
{ "to": "0x2",
"location": ["192.168.0.13"],
"reply-to": self.listener.location, }, ]
self.assertEqual(self.sender.send(), expected)
class TestConsuming(SantiagoTest):
"""TODO: tests for:
(learning services)
"""
pass
class TestInitialRequest(SantiagoTest):
"""Testing the initial request.
Does Santiago produce well-formed output when creating a service request?
"""
def setup(self):
requester = santiago.SantiagoSender("initial_requester")
destination = santiago.SantiagoListener("initial_destination",8081)
def test_valid_request(self):
"""When requested, does Santiago send out an appropriate message?"""
# TODO finish these and otehr tests
requester.request(destination="james", service="wiki")
class TestInitialResponse(unittest.TestCase):
pass
class TestForwardedRequest(unittest.TestCase):
class TestInitialResponse(SantiagoTest):
pass
class TestForwardedResponse(unittest.TestCase):
class TestForwardedRequest(SantiagoTest):
pass
class TestForwardedResponse(SantiagoTest):
pass
if __name__ == "__main__":
unittest.main()