~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 23:01:16 UTC
  • Revision ID: curtis.hovey@canonical.com-20120317230116-vjf7ztzwg0asr2x0
Use wsgiref.headers.

Show diffs side-by-side

added added

removed removed

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