~unity-2d-team/unity-2d/Shell-MultiMonitor

« back to all changes in this revision

Viewing changes to grackle/tests/test_client.py

  • Committer: Curtis Hovey
  • Date: 2012-01-30 18:30:54 UTC
  • mto: (6.1.30 trunk)
  • mto: This revision was merged to the branch mainline in revision 45.
  • Revision ID: curtis.hovey@canonical.com-20120130183054-lyjgaxcekr8efuou
Hush lint.

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
    HTTPServer,
3
3
    BaseHTTPRequestHandler,
4
4
    )
5
 
from email.message import Message
6
 
from email.mime.multipart import MIMEMultipart
7
 
from email.mime.text import MIMEText
8
5
import httplib
9
6
import logging
10
7
import os
14
11
import sys
15
12
from unittest import TestCase
16
13
from urlparse import urlparse
 
14
from urlparse import parse_qs
17
15
 
18
16
from testtools import ExpectedException
19
17
 
20
18
from grackle.client import (
21
19
    GrackleClient,
22
 
    UnparsableDateRange,
23
 
    UnsupportedDisplayType,
24
20
    UnsupportedOrder,
25
21
    )
26
 
from grackle.store import (
27
 
    MemoryStore,
28
 
    )
29
 
 
30
 
 
31
 
def make_message(message_id, body='body', headers=None, hidden=False):
32
 
    if headers is None:
33
 
        headers = {}
34
 
    headers['Message-Id'] = message_id
35
 
    message = {
36
 
        'message_id': message_id,
37
 
        'headers': headers,
38
 
        'thread_id': message_id,
39
 
        'date': headers.get('date', '2005-01-01'),
40
 
        'subject': headers.get('subject', 'subject'),
41
 
        'author': headers.get('author', 'author'),
42
 
        'hidden': hidden,
43
 
        'attachments': [],
44
 
        'replies': headers.get('in-reply-to', None),
45
 
        'body': body,
46
 
        }
47
 
    return message
48
 
 
49
 
 
50
 
def make_mime_message(message_id, body='body', headers=None, hidden=False,
51
 
                      attachment_type=None):
52
 
    message = MIMEMultipart()
53
 
    message.attach(MIMEText(body))
54
 
    if attachment_type is not None:
55
 
        attachment = Message()
56
 
        attachment.set_payload('attactment data.')
57
 
        attachment['Content-Type'] = attachment_type
58
 
        attachment['Content-Disposition'] = 'attachment; filename="file.ext"'
59
 
        message.attach(attachment)
60
 
    return make_message(message_id, message.get_payload(), headers, hidden)
61
 
 
62
 
 
63
 
class ForkedFakeService:
 
22
 
 
23
 
 
24
def threaded_messages(messages):
 
25
    threads = {}
 
26
    count = 0
 
27
    pending = []
 
28
    for message in messages:
 
29
        if message.get('in_reply_to') is None:
 
30
            threads[message['message_id']] = [message]
 
31
            count += 1
 
32
        else:
 
33
            pending.append(message)
 
34
    for message in pending:
 
35
        threads[message['in_reply_to']].append(message)
 
36
    return threads.values()
 
37
 
 
38
 
 
39
class GrackleStore:
 
40
    """A memory-backed message store."""
 
41
 
 
42
    def __init__(self, messages):
 
43
        """Constructor."""
 
44
        self.messages = messages
 
45
 
 
46
    def get_messages(self, archive_id, query_string):
 
47
        """Return matching messages.
 
48
 
 
49
        :param archive_id: The archive to retrieve from.
 
50
        :param query_string: Contains 'parameters', which is a JSON-format
 
51
            string describing parameters.
 
52
        """
 
53
        query = parse_qs(query_string)
 
54
        parameters = simplejson.loads(query['parameters'][0])
 
55
        order = parameters.get('order')
 
56
        messages = self.messages[archive_id]
 
57
        if order is not None:
 
58
            if order not in SUPPORTED_ORDERS:
 
59
                raise UnsupportedOrder
 
60
            elif order.startswith('thread_'):
 
61
                threaded = threaded_messages(messages)
 
62
                messages = []
 
63
                if order == 'thread_subject':
 
64
                    threaded.sort(key=lambda t: t[0]['subject'])
 
65
                if order == 'thread_oldest':
 
66
                    threaded.sort(key=lambda t: min(m['date'] for m in t))
 
67
                if order == 'thread_newest':
 
68
                    threaded.sort(key=lambda t: max(m['date'] for m in t))
 
69
                for thread in threaded:
 
70
                    messages.extend(thread)
 
71
            else:
 
72
                messages.sort(key=lambda m: m[order])
 
73
        new_messages = []
 
74
        for message in messages:
 
75
            if (not parameters['include_hidden']
 
76
                and message.get('hidden', False)):
 
77
                continue
 
78
 
 
79
            if ('message_ids' in parameters
 
80
                and message['message_id'] not in parameters['message_ids']):
 
81
                continue
 
82
            message = dict(message)
 
83
            if 'headers' in parameters:
 
84
                headers = dict(
 
85
                    (k, v) for k, v in message['headers'].iteritems()
 
86
                    if k in parameters['headers'])
 
87
                message['headers'] = headers
 
88
            max_body = parameters.get('max_body_length')
 
89
            if max_body is not None:
 
90
                message['body'] = message['body'][:max_body]
 
91
            new_messages.append(message)
 
92
        messages = new_messages
 
93
        limit = parameters.get('limit', 100)
 
94
        memo = parameters.get('memo')
 
95
        message_id_indices = dict(
 
96
            (m['message_id'], idx) for idx, m in enumerate(messages))
 
97
        if memo is None:
 
98
            start = 0
 
99
        else:
 
100
            start = message_id_indices[memo.encode('rot13')]
 
101
        if start > 0:
 
102
            previous_memo = messages[start - 1]['message_id'].encode('rot13')
 
103
        else:
 
104
            previous_memo = None
 
105
        end = min(start + limit, len(messages))
 
106
        if end < len(messages):
 
107
            next_memo = messages[end]['message_id'].encode('rot13')
 
108
        else:
 
109
            next_memo = None
 
110
        messages = messages[start:end]
 
111
 
 
112
        response = {
 
113
            'messages': messages,
 
114
            'next_memo': next_memo,
 
115
            'previous_memo': previous_memo
 
116
            }
 
117
        return response
 
118
 
 
119
 
 
120
class ForkedFake:
64
121
    """A Grackle service fake, as a ContextManager."""
65
122
 
66
123
    def __init__(self, port, messages=None, write_logs=False):
67
124
        """Constructor.
68
 
 
69
 
        :param port: The tcp port to use.
 
125
        :param port: The tcp port to use
70
126
        :param messages: A dict of lists of dicts representing messages.  The
71
127
            outer dict represents the archive, the list represents the list of
72
128
            messages for that archive.
83
139
 
84
140
    @staticmethod
85
141
    def from_client(client, messages=None):
86
 
        """Instantiate a ForkedFakeService from the client.
 
142
        """Instantiate a ForkedFake from the client.
87
143
 
88
 
        :param port: The client to provide service for.
 
144
        :param port: The client  to provide service for.
89
145
        :param messages: A dict of lists of dicts representing messages.  The
90
146
            outer dict represents the archive, the list represents the list of
91
147
            messages for that archive.
92
148
        """
93
 
        return ForkedFakeService(client.port, messages)
 
149
        return ForkedFake(client.port, messages)
94
150
 
95
151
    def is_ready(self):
96
152
        """Tell the parent process that the server is ready for writes."""
111
167
    def start_server(self):
112
168
        """Start the HTTP server."""
113
169
        service = HTTPServer(('', self.port), FakeGrackleRequestHandler)
114
 
        service.store = MemoryStore(self.messages)
 
170
        service.store = GrackleStore(self.messages)
115
171
        for archive_id, messages in service.store.messages.iteritems():
116
172
            for message in messages:
117
173
                message.setdefault('headers', {})
125
181
        os.kill(self.pid, SIGKILL)
126
182
 
127
183
 
 
184
SUPPORTED_ORDERS = set(
 
185
    ['date', 'author', 'subject', 'thread_newest', 'thread_oldest',
 
186
     'thread_subject'])
 
187
 
 
188
 
128
189
class FakeGrackleRequestHandler(BaseHTTPRequestHandler):
129
190
    """A request handler that forwards to server.store."""
130
191
 
155
216
                self.send_response(httplib.OK)
156
217
                self.end_headers()
157
218
                self.wfile.write(simplejson.dumps(response))
158
 
            except Exception, error:
159
 
                self.send_response(
160
 
                    httplib.BAD_REQUEST, error.__doc__)
 
219
            except UnsupportedOrder:
 
220
                self.send_response(httplib.BAD_REQUEST)
 
221
                self.wfile.write('Unsupported order')
161
222
                return
162
223
 
163
224
    def log_message(self, format, *args):
170
231
class TestPutMessage(TestCase):
171
232
 
172
233
    def test_put_message(self):
173
 
        client = GrackleClient('localhost', 8420)
174
 
        with ForkedFakeService.from_client(client):
 
234
        client = GrackleClient('localhost', 8436)
 
235
        with ForkedFake.from_client(client):
175
236
            client.put_message('arch1', 'asdf', StringIO('This is a message'))
176
237
            with ExpectedException(Exception, 'wtf'):
177
238
                client.put_message('arch1', 'asdf',
188
249
            sorted(ids), sorted(messages, key=lambda m: m['message_id']))
189
250
 
190
251
    def test_get_messages(self):
191
 
        client = GrackleClient('localhost', 8430)
192
 
        archive = {
193
 
            'baz': [make_message('foo'), make_message('bar')]}
194
 
        with ForkedFakeService.from_client(client, archive):
 
252
        client = GrackleClient('localhost', 8435)
 
253
        with ForkedFake.from_client(client,
 
254
            {'baz':
 
255
            [{'message_id': 'foo'},
 
256
             {'message_id': 'bar'}]}):
195
257
            response = client.get_messages('baz')
196
258
        self.assertEqual(['bar', 'foo'], sorted(m['message_id'] for m in
197
259
            response['messages']))
200
262
 
201
263
    def test_get_messages_by_id(self):
202
264
        client = GrackleClient('localhost', 8437)
203
 
        archive = {
204
 
            'baz': [make_message('foo'), make_message('bar')]}
205
 
        with ForkedFakeService.from_client(client, archive):
 
265
        with ForkedFake.from_client(client,
 
266
            {'baz':
 
267
            [{'message_id': 'foo'},
 
268
             {'message_id': 'bar'}]}):
206
269
            response = client.get_messages('baz', message_ids=['foo'])
207
270
        message, = response['messages']
208
271
        self.assertEqual('foo', message['message_id'])
209
272
 
210
273
    def test_get_messages_batching(self):
211
274
        client = GrackleClient('localhost', 8438)
212
 
        archive = {'baz': [make_message('foo'), make_message('bar')]}
213
 
        with ForkedFakeService.from_client(client, archive):
 
275
        with ForkedFake.from_client(client,
 
276
            {'baz':
 
277
            [{'message_id': 'foo'},
 
278
             {'message_id': 'bar'}]}):
214
279
            response = client.get_messages('baz', limit=1)
215
280
            self.assertEqual(1, len(response['messages']))
216
281
            messages = response['messages']
222
287
 
223
288
    def get_messages_member_order_test(self, key):
224
289
        client = GrackleClient('localhost', 8439)
225
 
        archive = {
226
 
            'baz': [
227
 
                make_message('foo', headers={key: '2011-03-25'}),
228
 
                make_message('bar', headers={key: '2011-03-24'}),
229
 
             ]}
230
 
        with ForkedFakeService.from_client(client, archive):
 
290
        with ForkedFake.from_client(client,
 
291
                {'baz': [{'message_id': 'foo', key: '2011-03-25'},
 
292
                 {'message_id': 'bar', key: '2011-03-24'}]}):
231
293
            response = client.get_messages('baz')
232
294
            self.assertIDOrder(['foo', 'bar'], response['messages'])
233
295
            response = client.get_messages('baz', order=key)
243
305
        self.get_messages_member_order_test('subject')
244
306
 
245
307
    def test_get_messages_thread_subject_order(self):
246
 
        archive = {
247
 
            'baz': [
248
 
                make_message('bar', headers={'subject': 'y'}),
249
 
                make_message('qux', headers={'subject': 'z'}),
250
 
                make_message('foo', headers={'subject': 'x',
251
 
                                             'in-reply-to': 'qux'}),
252
 
             ]}
253
308
        client = GrackleClient('localhost', 8439)
254
 
        with ForkedFakeService.from_client(client, archive):
 
309
        with ForkedFake.from_client(client, {'baz': [
 
310
            {'message_id': 'bar', 'subject': 'y'},
 
311
            {'message_id': 'qux', 'subject': 'z'},
 
312
            {'message_id': 'foo', 'subject': 'x', 'in_reply_to': 'qux'},
 
313
            ]}):
255
314
            response = client.get_messages('baz')
256
315
            self.assertIDOrder(['bar', 'qux', 'foo'], response['messages'])
257
316
            response = client.get_messages('baz', order='subject')
261
320
 
262
321
    def test_get_messages_thread_oldest_order(self):
263
322
        client = GrackleClient('localhost', 8439)
264
 
        archive = {
265
 
            'baz': [
266
 
                make_message('bar', headers={'date': 'x'}),
267
 
                make_message('qux', headers={'date': 'z'}),
268
 
                make_message('foo', headers={'date': 'y',
269
 
                                             'in-reply-to': 'qux'}),
270
 
            ]}
271
 
        with ForkedFakeService.from_client(client, archive):
 
323
        with ForkedFake.from_client(client, {'baz': [
 
324
            {'message_id': 'bar', 'date': 'x'},
 
325
            {'message_id': 'qux', 'date': 'z'},
 
326
            {'message_id': 'foo', 'date': 'y', 'in_reply_to': 'qux'},
 
327
            ]}):
272
328
            response = client.get_messages('baz')
273
329
            self.assertIDOrder(['bar', 'qux', 'foo'], response['messages'])
274
330
            response = client.get_messages('baz', order='date')
278
334
 
279
335
    def test_get_messages_thread_newest_order(self):
280
336
        client = GrackleClient('localhost', 8439)
281
 
        archive = {
282
 
            'baz': [
283
 
                make_message('bar', headers={'date': 'x'}),
284
 
                make_message('qux', headers={'date': 'w'}),
285
 
                make_message('foo', headers={'date': 'y',
286
 
                                             'in-reply-to': 'bar'}),
287
 
                make_message('baz', headers={'date': 'z',
288
 
                                             'in-reply-to': 'qux'}),
289
 
            ]}
290
 
        with ForkedFakeService.from_client(client, archive):
 
337
        with ForkedFake.from_client(client, {'baz': [
 
338
            {'message_id': 'bar', 'date': 'x'},
 
339
            {'message_id': 'qux', 'date': 'w'},
 
340
            {'message_id': 'foo', 'date': 'y', 'in_reply_to': 'bar'},
 
341
            {'message_id': 'baz', 'date': 'z', 'in_reply_to': 'qux'},
 
342
            ]}):
291
343
            response = client.get_messages('baz', order='date')
292
344
            self.assertIDOrder(
293
345
                ['qux', 'bar', 'foo', 'baz'], response['messages'])
297
349
 
298
350
    def test_get_messages_unsupported_order(self):
299
351
        client = GrackleClient('localhost', 8439)
300
 
        archive = {
301
 
            'baz': [
302
 
                make_message('foo', headers={'date': '2011-03-25'}),
303
 
                make_message('foo', headers={'date': '2011-03-24'}),
304
 
            ]}
305
 
        with ForkedFakeService.from_client(client, archive):
 
352
        with ForkedFake.from_client(client,
 
353
                {'baz': [{'message_id': 'foo', 'date': '2011-03-25'},
 
354
                 {'message_id': 'bar', 'date': '2011-03-24'}]}):
306
355
            with ExpectedException(UnsupportedOrder, ''):
307
356
                client.get_messages('baz', order='nonsense')
308
357
 
309
358
    def test_get_messages_headers_no_headers(self):
310
359
        client = GrackleClient('localhost', 8440)
311
 
        archive = {'baz': [make_message('foo')]}
312
 
        with ForkedFakeService.from_client(client, archive):
 
360
        with ForkedFake.from_client(client,
 
361
            {'baz': [
 
362
                {'message_id': 'foo'}
 
363
            ]}):
313
364
            response = client.get_messages('baz', headers=[
314
365
                'Subject', 'Date', 'X-Launchpad-Message-Rationale'])
315
366
        first_message = response['messages'][0]
318
369
 
319
370
    def test_get_messages_headers_exclude_headers(self):
320
371
        client = GrackleClient('localhost', 8441)
321
 
        archive = {
322
 
            'baz': [make_message('foo', headers={'From': 'me'})]}
323
 
        with ForkedFakeService.from_client(client, archive):
 
372
        with ForkedFake.from_client(client,
 
373
            {'baz': [
 
374
                {'message_id': 'foo', 'headers': {'From': 'me'}}
 
375
            ]}):
324
376
            response = client.get_messages('baz', headers=[
325
377
                'Subject', 'Date', 'X-Launchpad-Message-Rationale'])
326
378
        first_message = response['messages'][0]
329
381
 
330
382
    def test_get_messages_headers_include_headers(self):
331
383
        client = GrackleClient('localhost', 8442)
332
 
        archive = {
333
 
            'baz': [
334
 
                make_message('foo', headers={'From': 'me', 'To': 'you'})]}
335
 
        with ForkedFakeService.from_client(client, archive):
 
384
        with ForkedFake.from_client(client,
 
385
            {'baz': [
 
386
                {'message_id': 'foo', 'headers': {'From': 'me', 'To': 'you'}}
 
387
            ]}):
336
388
            response = client.get_messages('baz', headers=[
337
389
                'From', 'To'])
338
390
        first_message = response['messages'][0]
341
393
 
342
394
    def test_get_messages_max_body_length(self):
343
395
        client = GrackleClient('localhost', 8443)
344
 
        archive = {'baz': [make_message('foo', body=u'abcdefghi')]}
345
 
        with ForkedFakeService.from_client(client, archive):
 
396
        with ForkedFake.from_client(client,
 
397
            {'baz': [
 
398
                {'message_id': 'foo', 'body': u'abcdefghi'}
 
399
            ]}):
346
400
            response = client.get_messages('baz', max_body_length=3)
347
401
        first_message = response['messages'][0]
348
402
        self.assertEqual('abc', first_message['body'])
349
403
 
350
404
    def test_include_hidden(self):
351
405
        client = GrackleClient('localhost', 8444)
352
 
        archive = {
353
 
            'baz': [
354
 
                make_message('foo', hidden=True),
355
 
                make_message('bar', hidden=False),
356
 
            ]}
357
 
        with ForkedFakeService.from_client(client, archive):
 
406
        with ForkedFake.from_client(client,
 
407
            {'baz': [
 
408
                {'message_id': 'foo', 'hidden': True},
 
409
                {'message_id': 'bar', 'hidden': False}
 
410
            ]}):
358
411
            response = client.get_messages('baz', include_hidden=True)
359
412
            self.assertMessageIDs(['bar', 'foo'], response['messages'])
360
413
            response = client.get_messages('baz', include_hidden=False)
361
414
            self.assertMessageIDs(['bar'], response['messages'])
362
 
 
363
 
    def test_display_type_unknown_value(self):
364
 
        client = GrackleClient('localhost', 8445)
365
 
        archive = {'baz': [make_message('foo', body=u'abcdefghi')]}
366
 
        with ForkedFakeService.from_client(client, archive):
367
 
            with ExpectedException(UnsupportedDisplayType, ''):
368
 
                client.get_messages('baz', display_type='unknown')
369
 
 
370
 
    def test_display_type_headers_only(self):
371
 
        client = GrackleClient('localhost', 8446)
372
 
        archive = {
373
 
            'baz': [
374
 
                make_message('foo', body=u'abcdefghi',
375
 
                             headers={'From': 'me', 'To': 'you'})]}
376
 
        with ForkedFakeService.from_client(client, archive):
377
 
            response = client.get_messages('baz', display_type='headers-only')
378
 
        first_message = response['messages'][0]
379
 
        self.assertEqual('foo', first_message['message_id'])
380
 
        self.assertEqual(
381
 
            {'From': 'me', 'Message-Id': 'foo', 'To': 'you'},
382
 
            first_message['headers'])
383
 
        self.assertNotIn('body', first_message)
384
 
 
385
 
    def test_display_type_text_only(self):
386
 
        client = GrackleClient('localhost', 8446)
387
 
        archive = {
388
 
            'baz': [
389
 
                make_mime_message(
390
 
                    'foo', 'abcdefghi',
391
 
                    headers={'From': 'me', 'To': 'you'},
392
 
                    attachment_type='text/x-diff')]}
393
 
        with ForkedFakeService.from_client(client, archive):
394
 
            response = client.get_messages('baz', display_type='text-only')
395
 
        first_message = response['messages'][0]
396
 
        self.assertEqual('foo', first_message['message_id'])
397
 
        self.assertEqual('me', first_message['headers']['From'])
398
 
        self.assertEqual('you', first_message['headers']['To'])
399
 
        self.assertEqual('abcdefghi', first_message['body'])
400
 
 
401
 
    def test_display_type_all(self):
402
 
        client = GrackleClient('localhost', 8447)
403
 
        archive = {
404
 
            'baz': [
405
 
                make_mime_message(
406
 
                    'foo', 'abcdefghi',
407
 
                    headers={'From': 'me', 'To': 'you'},
408
 
                    attachment_type='text/x-diff')]}
409
 
        with ForkedFakeService.from_client(client, archive):
410
 
            response = client.get_messages('baz', display_type='all')
411
 
        first_message = response['messages'][0]
412
 
        self.assertEqual('foo', first_message['message_id'])
413
 
        self.assertEqual('me', first_message['headers']['From'])
414
 
        self.assertEqual('you', first_message['headers']['To'])
415
 
        self.assertEqual(
416
 
            'abcdefghi\n\nattactment data.', first_message['body'])
417
 
 
418
 
    def test_date_range(self):
419
 
        client = GrackleClient('localhost', 8448)
420
 
        archive = {
421
 
            'baz': [
422
 
                make_mime_message(
423
 
                    'foo', 'abcdefghi', headers={'date': '2011-12-31'}),
424
 
                make_mime_message(
425
 
                    'bar', 'abcdefghi', headers={'date': '2012-01-01'}),
426
 
                make_mime_message(
427
 
                    'qux', 'abcdefghi', headers={'date': '2012-01-15'}),
428
 
                make_mime_message(
429
 
                    'naf', 'abcdefghi', headers={'date': '2012-01-31'}),
430
 
                make_mime_message(
431
 
                    'doh', 'abcdefghi', headers={'date': '2012-02-01'}),
432
 
                    ]}
433
 
        with ForkedFakeService.from_client(client, archive):
434
 
            response = client.get_messages(
435
 
                'baz', date_range='2012-01-01..2012-01-31')
436
 
        ids = sorted(m['message_id'] for m in response['messages'])
437
 
        self.assertEqual(['bar', 'naf', 'qux'], ids)
438
 
 
439
 
    def test_date_range_unparsabledaterange(self):
440
 
        client = GrackleClient('localhost', 8449)
441
 
        archive = {'baz': [make_message('foo', body=u'abcdefghi')]}
442
 
        with ForkedFakeService.from_client(client, archive):
443
 
            with ExpectedException(UnparsableDateRange, ''):
444
 
                client.get_messages('baz', date_range='2012-01-01')
445
 
 
446
 
    def test_date_range_unparsabledaterange_missing_part(self):
447
 
        client = GrackleClient('localhost', 8450)
448
 
        archive = {'baz': [make_message('foo', body=u'abcdefghi')]}
449
 
        with ForkedFakeService.from_client(client, archive):
450
 
            with ExpectedException(UnparsableDateRange, ''):
451
 
                client.get_messages('baz', date_range='2012-01-01..')
452
 
 
453
 
    def test_date_range_unparsabledaterange_extra_part(self):
454
 
        client = GrackleClient('localhost', 8451)
455
 
        archive = {'baz': [make_message('foo', body=u'abcdefghi')]}
456
 
        with ForkedFakeService.from_client(client, archive):
457
 
            with ExpectedException(UnparsableDateRange, ''):
458
 
                client.get_messages('baz', date_range='2012-01..12-02..12-03')