~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 16:10:54 UTC
  • Revision ID: curtis.hovey@canonical.com-20120316161054-jk68i83kh81qm2vi
Store the body text only.

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
 
11
 
from grackle.client import GrackleClient
12
 
from grackle.error import (
13
 
    ArchiveIdExists,
 
20
from grackle.client import (
 
21
    GrackleClient,
14
22
    UnparsableDateRange,
15
23
    UnsupportedDisplayType,
16
24
    UnsupportedOrder,
17
25
    )
18
 
from grackle.service import ForkedFakeService
19
 
from grackle.store import make_json_message
 
26
from grackle.store import (
 
27
    make_json_message,
 
28
    MemoryStore,
 
29
    )
20
30
 
21
31
 
22
32
def make_message(message_id, body='body', headers=None, hidden=False):
23
33
    if headers is None:
24
34
        headers = {}
25
 
    message_headers = {
26
 
        'Message-Id': message_id,
27
 
        'date': '2005-01-01',
28
 
        'subject': 'subject',
29
 
        'from': 'author',
30
 
        'replies': '',
 
35
    headers['Message-Id'] = message_id
 
36
    message = {
 
37
        'message_id': message_id,
 
38
        'headers': headers,
 
39
        'thread_id': message_id,
 
40
        'date': headers.get('date', '2005-01-01'),
 
41
        'subject': headers.get('subject', 'subject'),
 
42
        'author': headers.get('author', 'author'),
 
43
        'hidden': hidden,
 
44
        'attachments': [],
 
45
        'replies': headers.get('in-reply-to', None),
 
46
        'body': body,
31
47
        }
32
 
    message_headers.update(headers.items())
33
 
    message = Message()
34
 
    message.set_payload(body)
35
 
    for key, value in message_headers.items():
36
 
        message[key] = value
37
 
    return make_json_message(message_id, message.as_string(), hidden)
 
48
    return message
38
49
 
39
50
 
40
51
def make_mime_message(message_id, body='body', headers=None, hidden=False,
41
52
                      attachment_type=None):
 
53
    if headers is None:
 
54
        headers = {}
42
55
    parts = MIMEMultipart()
43
56
    parts.attach(MIMEText(body))
44
57
    if attachment_type is not None:
47
60
        attachment['Content-Type'] = attachment_type
48
61
        attachment['Content-Disposition'] = 'attachment; filename="file.ext"'
49
62
        parts.attach(attachment)
50
 
    return make_message(message_id, parts.as_string(), headers, hidden)
51
 
 
52
 
 
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
    message = Message()
 
64
    message.set_payload(parts.as_string())
 
65
    for key, value in headers.items():
 
66
        message[key] = value
 
67
    return make_json_message(message_id, message.as_string())
 
68
 
 
69
 
 
70
class ForkedFakeService:
 
71
    """A Grackle service fake, as a ContextManager."""
 
72
 
 
73
    def __init__(self, port, message_archives=None, write_logs=False):
 
74
        """Constructor.
 
75
 
 
76
        :param port: The tcp port to use.
 
77
        :param message_archives: A dict of lists of dicts representing
 
78
            archives of messages. The outer dict represents the archive,
 
79
            the list represents the list of messages for that archive.
 
80
        :param write_logs: If true, log messages will be written to stdout.
 
81
        """
 
82
        self.pid = None
 
83
        self.port = port
 
84
        if message_archives is None:
 
85
            self.message_archives = {}
 
86
        else:
 
87
            self.message_archives = message_archives
 
88
        self.read_end, self.write_end = os.pipe()
 
89
        self.write_logs = write_logs
 
90
 
 
91
    @staticmethod
 
92
    def from_client(client, message_archives=None):
 
93
        """Instantiate a ForkedFakeService from the client.
 
94
 
 
95
        :param port: The client to provide service for.
 
96
        :param message_archives: A dict of lists of dicts representing
 
97
            archives of messages. The outer dict represents the archive,
 
98
            the list represents the list of messages for that archive.
 
99
        """
 
100
        return ForkedFakeService(client.port, message_archives)
 
101
 
 
102
    def is_ready(self):
 
103
        """Tell the parent process that the server is ready for writes."""
 
104
        os.write(self.write_end, 'asdf')
 
105
 
 
106
    def __enter__(self):
 
107
        """Run the service.
 
108
 
 
109
        Fork and start a server in the child.  Return when the server is ready
 
110
        for use."""
 
111
        pid = os.fork()
 
112
        if pid == 0:
 
113
            self.start_server()
 
114
        self.pid = pid
 
115
        os.read(self.read_end, 1)
 
116
        return
 
117
 
 
118
    def start_server(self):
 
119
        """Start the HTTP server."""
 
120
        service = HTTPServer(('', self.port), FakeGrackleRequestHandler)
 
121
        service.store = MemoryStore(self.message_archives)
 
122
        for archive_id, messages in service.store.message_archives.iteritems():
 
123
            for message in messages:
 
124
                message.setdefault('headers', {})
 
125
        self.is_ready()
 
126
        if self.write_logs:
 
127
            logging.basicConfig(
 
128
                stream=sys.stderr, level=logging.INFO)
 
129
        service.serve_forever()
 
130
 
 
131
    def __exit__(self, exc_type, exc_val, traceback):
 
132
        os.kill(self.pid, SIGKILL)
 
133
 
 
134
 
 
135
class FakeGrackleRequestHandler(BaseHTTPRequestHandler):
 
136
    """A request handler that forwards to server.store."""
 
137
 
 
138
    def __init__(self, *args, **kwargs):
 
139
        """Constructor.  Sets up logging."""
 
140
        self.logger = logging.getLogger('http')
 
141
        BaseHTTPRequestHandler.__init__(self, *args, **kwargs)
 
142
 
 
143
    def do_POST(self):
 
144
        """Create a message on POST."""
 
145
        message = self.rfile.read(int(self.headers['content-length']))
 
146
        scheme, netloc, path, params, query_string, fragments = (
 
147
            urlparse(self.path))
 
148
        parts = path.split('/')
 
149
        if parts[1] == 'archive' and len(parts) == 4:
 
150
            try:
 
151
                # This expected path is /archive/archive_id/message_id.
 
152
                self.server.store.put_message(parts[2], parts[3], message)
 
153
                self.send_response(httplib.CREATED)
 
154
                self.end_headers()
 
155
                self.wfile.close()
 
156
            except:
 
157
                self.send_error(httplib.BAD_REQUEST)
 
158
 
 
159
    def do_GET(self):
 
160
        """Retrieve a list of messages on GET."""
 
161
        scheme, netloc, path, params, query_string, fragments = (
 
162
            urlparse(self.path))
 
163
        parts = path.split('/')
 
164
        if parts[1] == 'archive':
 
165
            try:
 
166
                response = self.server.store.get_messages(
 
167
                    parts[2], query_string)
 
168
                self.send_response(httplib.OK)
 
169
                self.end_headers()
 
170
                self.wfile.write(simplejson.dumps(response))
 
171
            except Exception, error:
 
172
                self.send_response(
 
173
                    httplib.BAD_REQUEST, error.__doc__)
 
174
                return
 
175
 
 
176
    def log_message(self, format, *args):
 
177
        """Override log_message to use standard Python logging."""
 
178
        message = "%s - - [%s] %s\n" % (
 
179
            self.address_string(), self.log_date_time_string(), format % args)
 
180
        self.logger.info(message)
69
181
 
70
182
 
71
183
class TestPutMessage(TestCase):
132
244
 
133
245
    def get_messages_member_order_test(self, key):
134
246
        client = GrackleClient('localhost', 8439)
135
 
        if key == 'author':
136
 
            header_name = 'from'
137
 
        else:
138
 
            header_name = key
139
247
        archive = {
140
248
            'baz': [
141
 
                make_message('foo', headers={header_name: '2011-03-25'}),
142
 
                make_message('bar', headers={header_name: '2011-03-24'}),
 
249
                make_message('foo', headers={key: '2011-03-25'}),
 
250
                make_message('bar', headers={key: '2011-03-24'}),
143
251
             ]}
144
252
        with ForkedFakeService.from_client(client, archive):
145
253
            response = client.get_messages('baz')
292
400
        first_message = response['messages'][0]
293
401
        self.assertEqual('foo', first_message['message_id'])
294
402
        self.assertEqual(
295
 
            archive['baz'][0]['headers'], first_message['headers'])
 
403
            {'From': 'me', 'Message-Id': 'foo', 'To': 'you'},
 
404
            first_message['headers'])
296
405
        self.assertNotIn('body', first_message)
297
406
 
298
407
    def test_display_type_text_only(self):
368
477
        with ForkedFakeService.from_client(client, archive):
369
478
            with ExpectedException(UnparsableDateRange, ''):
370
479
                client.get_messages('baz', date_range='2012-01..12-02..12-03')
371
 
 
372
 
 
373
 
class TestHideMessages(TestCase):
374
 
 
375
 
    def test_hide_message_true(self):
376
 
        client = GrackleClient('localhost', 8470)
377
 
        archive = {
378
 
            'baz': [
379
 
                make_message('foo', hidden=False),
380
 
            ]}
381
 
        with ForkedFakeService.from_client(client, archive):
382
 
            response = client.hide_message('baz', 'foo', hidden=True)
383
 
        self.assertEqual('foo', response['message_id'])
384
 
        self.assertIs(True, response['hidden'])
385
 
 
386
 
    def test_hide_message_false(self):
387
 
        client = GrackleClient('localhost', 8470)
388
 
        archive = {
389
 
            'baz': [
390
 
                make_message('foo', hidden=True),
391
 
            ]}
392
 
        with ForkedFakeService.from_client(client, archive):
393
 
            response = client.hide_message('baz', 'foo', hidden=False)
394
 
        self.assertEqual('foo', response['message_id'])
395
 
        self.assertIs(False, response['hidden'])