~didrocks/unity/altf10

« back to all changes in this revision

Viewing changes to grackle/tests/test_client.py

  • Committer: Curtis Hovey
  • Date: 2012-03-16 19:49:31 UTC
  • Revision ID: curtis.hovey@canonical.com-20120316194931-xngt1fdw9ewovwqn
Use PUT for creating messages.

Show diffs side-by-side

added added

removed removed

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