~didrocks/unity/altf10

« back to all changes in this revision

Viewing changes to grackle/tests/test_client.py

  • Committer: Curtis Hovey
  • Date: 2012-03-17 20:57:24 UTC
  • Revision ID: curtis.hovey@canonical.com-20120317205724-2tqfvj1jqtn9fr8t
Converted gracle from a request handler to a wsgi app.
Keep Arrons brilliant ForkedFakeService.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
__metaclass__ = type
2
 
 
 
1
from BaseHTTPServer import (
 
2
    HTTPServer,
 
3
    BaseHTTPRequestHandler,
 
4
    )
3
5
from email.message import Message
4
6
from email.mime.multipart import MIMEMultipart
5
7
from email.mime.text import MIMEText
 
8
import httplib
 
9
import logging
 
10
import os
 
11
from signal import SIGKILL
 
12
import simplejson
6
13
from StringIO import StringIO
 
14
import sys
7
15
from unittest import TestCase
 
16
from urlparse import urlparse
8
17
 
9
18
from testtools import ExpectedException
10
19
 
16
25
    UnsupportedOrder,
17
26
    )
18
27
from grackle.service import ForkedFakeService
19
 
from grackle.store import make_json_message
 
28
from grackle.store import (
 
29
    make_json_message,
 
30
    MemoryStore,
 
31
    )
20
32
 
21
33
 
22
34
def make_message(message_id, body='body', headers=None, hidden=False):
50
62
    return make_message(message_id, parts.as_string(), headers, hidden)
51
63
 
52
64
 
 
65
class XXXForkedFakeService:
 
66
    """A Grackle service fake, as a ContextManager."""
 
67
 
 
68
    def __init__(self, port, message_archives=None, write_logs=False):
 
69
        """Constructor.
 
70
 
 
71
        :param port: The tcp port to use.
 
72
        :param message_archives: A dict of lists of dicts representing
 
73
            archives of messages. The outer dict represents the archive,
 
74
            the list represents the list of messages for that archive.
 
75
        :param write_logs: If true, log messages will be written to stdout.
 
76
        """
 
77
        self.pid = None
 
78
        self.port = port
 
79
        if message_archives is None:
 
80
            self.message_archives = {}
 
81
        else:
 
82
            self.message_archives = message_archives
 
83
        self.read_end, self.write_end = os.pipe()
 
84
        self.write_logs = write_logs
 
85
 
 
86
    @staticmethod
 
87
    def from_client(client, message_archives=None):
 
88
        """Instantiate a ForkedFakeService from the client.
 
89
 
 
90
        :param port: The client to provide service for.
 
91
        :param message_archives: A dict of lists of dicts representing
 
92
            archives of messages. The outer dict represents the archive,
 
93
            the list represents the list of messages for that archive.
 
94
        """
 
95
        return ForkedFakeService(client.port, message_archives)
 
96
 
 
97
    def is_ready(self):
 
98
        """Tell the parent process that the server is ready for writes."""
 
99
        os.write(self.write_end, 'asdf')
 
100
 
 
101
    def __enter__(self):
 
102
        """Run the service.
 
103
 
 
104
        Fork and start a server in the child.  Return when the server is ready
 
105
        for use."""
 
106
        pid = os.fork()
 
107
        if pid == 0:
 
108
            self.start_server()
 
109
        self.pid = pid
 
110
        os.read(self.read_end, 1)
 
111
        return
 
112
 
 
113
    def start_server(self):
 
114
        """Start the HTTP server."""
 
115
        service = HTTPServer(('', self.port), FakeGrackleRequestHandler)
 
116
        service.store = MemoryStore(self.message_archives)
 
117
        self.is_ready()
 
118
        if self.write_logs:
 
119
            logging.basicConfig(
 
120
                stream=sys.stderr, level=logging.INFO)
 
121
        service.serve_forever()
 
122
 
 
123
    def __exit__(self, exc_type, exc_val, traceback):
 
124
        os.kill(self.pid, SIGKILL)
 
125
 
 
126
 
 
127
class FakeGrackleRequestHandler(BaseHTTPRequestHandler):
 
128
    """A request handler that forwards to server.store."""
 
129
 
 
130
    def __init__(self, *args, **kwargs):
 
131
        """Constructor.  Sets up logging."""
 
132
        self.logger = logging.getLogger('http')
 
133
        BaseHTTPRequestHandler.__init__(self, *args, **kwargs)
 
134
 
 
135
    def do_PUT(self):
 
136
        """Create an archive or message on PUT."""
 
137
        scheme, netloc, path, params, query_string, fragments = (
 
138
            urlparse(self.path))
 
139
        parts = path.split('/')
 
140
        if parts[1] != 'archive':
 
141
            # This is an unknonwn operation?
 
142
            return
 
143
        if len(parts) == 3:
 
144
            # This expected path is /archive/archive_id.
 
145
            try:
 
146
                self.server.store.put_archive(parts[2])
 
147
                self.send_response(httplib.CREATED)
 
148
                self.end_headers()
 
149
                self.wfile.close()
 
150
            except Exception, error:
 
151
                self.send_response(
 
152
                    httplib.BAD_REQUEST, error.__doc__)
 
153
        if len(parts) == 4:
 
154
            # This expected path is /archive/archive_id/message_id.
 
155
            try:
 
156
                message = self.rfile.read(int(self.headers['content-length']))
 
157
                self.server.store.put_message(parts[2], parts[3], message)
 
158
                self.send_response(httplib.CREATED)
 
159
                self.end_headers()
 
160
                self.wfile.close()
 
161
            except:
 
162
                self.send_error(httplib.BAD_REQUEST)
 
163
 
 
164
    def do_POST(self):
 
165
        """Change a message on POST."""
 
166
        scheme, netloc, path, params, query_string, fragments = (
 
167
            urlparse(self.path))
 
168
        parts = path.split('/')
 
169
        if parts[1] != 'archive':
 
170
            # This is an unknonwn operation?
 
171
            return
 
172
        if len(parts) == 4:
 
173
            # This expected path is /archive/archive_id/message_id.
 
174
            try:
 
175
                # This expected path is /archive/archive_id/message_id.
 
176
                response = self.server.store.hide_message(
 
177
                    parts[2], parts[3], query_string)
 
178
                self.send_response(httplib.OK)
 
179
                self.end_headers()
 
180
                self.wfile.write(simplejson.dumps(response))
 
181
            except:
 
182
                self.send_error(httplib.BAD_REQUEST)
 
183
 
 
184
    def do_GET(self):
 
185
        """Retrieve a list of messages on GET."""
 
186
        scheme, netloc, path, params, query_string, fragments = (
 
187
            urlparse(self.path))
 
188
        parts = path.split('/')
 
189
        if parts[1] == 'archive':
 
190
            try:
 
191
                response = self.server.store.get_messages(
 
192
                    parts[2], query_string)
 
193
                self.send_response(httplib.OK)
 
194
                self.end_headers()
 
195
                self.wfile.write(simplejson.dumps(response))
 
196
            except Exception, error:
 
197
                self.send_response(
 
198
                    httplib.BAD_REQUEST, error.__doc__)
 
199
                return
 
200
 
 
201
    def log_message(self, format, *args):
 
202
        """Override log_message to use standard Python logging."""
 
203
        message = "%s - - [%s] %s\n" % (
 
204
            self.address_string(), self.log_date_time_string(), format % args)
 
205
        self.logger.info(message)
 
206
 
 
207
 
53
208
class TestPutArchive(TestCase):
54
209
 
55
210
    def test_put_archive(self):