~didrocks/unity/altf10

« back to all changes in this revision

Viewing changes to grackle/tests/test_client.py

  • Committer: Curtis Hovey
  • Date: 2012-01-31 05:24:10 UTC
  • mfrom: (35.1.19 client-get-messages-0)
  • Revision ID: curtis.hovey@canonical.com-20120131052410-4n5iva4ujik6nhp8
Added support for display_type.
Introduced make_message and make_mime_message to make test data consistent for
InMemoryStore.

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
5
8
import httplib
6
9
import logging
7
10
import os
17
20
 
18
21
from grackle.client import (
19
22
    GrackleClient,
 
23
    UnsupportedDisplayType,
20
24
    UnsupportedOrder,
21
25
    )
22
26
 
23
27
 
 
28
def make_message(message_id, body='body', headers=None, hidden=False):
 
29
    if headers is None:
 
30
        headers = {}
 
31
    headers['Message-Id'] = message_id
 
32
    message = {
 
33
        'message_id': message_id,
 
34
        'headers': headers,
 
35
        'thread_id': message_id,
 
36
        'date': headers.get('date', '2005-01-01'),
 
37
        'subject': headers.get('subject', 'subject'),
 
38
        'author': headers.get('author', 'author'),
 
39
        'hidden': hidden,
 
40
        'attachments': [],
 
41
        'replies': headers.get('in-reply-to', None),
 
42
        'body': body,
 
43
        }
 
44
    return message
 
45
 
 
46
 
 
47
def make_mime_message(message_id, body='body', headers=None, hidden=False,
 
48
                      attachment_type=None):
 
49
    message = MIMEMultipart()
 
50
    message.attach(MIMEText(body))
 
51
    if attachment_type is not None:
 
52
        attachment = Message()
 
53
        attachment.set_payload('attactment data.')
 
54
        attachment['Content-Type'] = attachment_type
 
55
        attachment['Content-Disposition'] = 'attachment; filename="file.ext"'
 
56
        message.attach(attachment)
 
57
    return make_message(message_id, message.get_payload(), headers, hidden)
 
58
 
 
59
 
24
60
def threaded_messages(messages):
25
61
    threads = {}
26
62
    count = 0
27
63
    pending = []
28
64
    for message in messages:
29
 
        if message.get('in_reply_to') is None:
 
65
        if message.get('replies') is None:
30
66
            threads[message['message_id']] = [message]
31
67
            count += 1
32
68
        else:
33
69
            pending.append(message)
34
70
    for message in pending:
35
 
        threads[message['in_reply_to']].append(message)
 
71
        threads[message['replies']].append(message)
36
72
    return threads.values()
37
73
 
38
74
 
43
79
        """Constructor."""
44
80
        self.messages = messages
45
81
 
 
82
    @staticmethod
 
83
    def is_multipart(message):
 
84
        return isinstance(message['body'], list)
 
85
 
46
86
    def get_messages(self, archive_id, query_string):
47
87
        """Return matching messages.
48
88
 
70
110
                    messages.extend(thread)
71
111
            else:
72
112
                messages.sort(key=lambda m: m[order])
 
113
        display_type = parameters.get('display_type', 'all')
 
114
        if display_type not in SUPPORTED_DISPLAY_TYPES:
 
115
            raise UnsupportedDisplayType
73
116
        new_messages = []
74
117
        for message in messages:
75
 
            if (not parameters['include_hidden']
76
 
                and message.get('hidden', False)):
 
118
            if (not parameters['include_hidden'] and message['hidden']):
77
119
                continue
78
 
 
79
120
            if ('message_ids' in parameters
80
121
                and message['message_id'] not in parameters['message_ids']):
81
122
                continue
85
126
                    (k, v) for k, v in message['headers'].iteritems()
86
127
                    if k in parameters['headers'])
87
128
                message['headers'] = headers
 
129
            if display_type == 'headers-only':
 
130
                del message['body']
 
131
            elif display_type == 'text-only' and self.is_multipart(message):
 
132
                text_parts = [
 
133
                    part.get_payload() for part in message['body']
 
134
                    if part.get_content_type() == 'text/plain']
 
135
                message['body'] = '\n\n'.join(text_parts)
 
136
            elif display_type == 'all' and self.is_multipart(message):
 
137
                parts = [str(part.get_payload()) for part in message['body']]
 
138
                message['body'] = '\n\n'.join(parts)
88
139
            max_body = parameters.get('max_body_length')
89
 
            if max_body is not None:
 
140
            if max_body is not None and display_type != 'headers-only':
90
141
                message['body'] = message['body'][:max_body]
91
142
            new_messages.append(message)
92
143
        messages = new_messages
117
168
        return response
118
169
 
119
170
 
120
 
class ForkedFake:
 
171
class ForkedFakeService:
121
172
    """A Grackle service fake, as a ContextManager."""
122
173
 
123
174
    def __init__(self, port, messages=None, write_logs=False):
124
175
        """Constructor.
125
 
        :param port: The tcp port to use
 
176
 
 
177
        :param port: The tcp port to use.
126
178
        :param messages: A dict of lists of dicts representing messages.  The
127
179
            outer dict represents the archive, the list represents the list of
128
180
            messages for that archive.
139
191
 
140
192
    @staticmethod
141
193
    def from_client(client, messages=None):
142
 
        """Instantiate a ForkedFake from the client.
 
194
        """Instantiate a ForkedFakeService from the client.
143
195
 
144
 
        :param port: The client  to provide service for.
 
196
        :param port: The client to provide service for.
145
197
        :param messages: A dict of lists of dicts representing messages.  The
146
198
            outer dict represents the archive, the list represents the list of
147
199
            messages for that archive.
148
200
        """
149
 
        return ForkedFake(client.port, messages)
 
201
        return ForkedFakeService(client.port, messages)
150
202
 
151
203
    def is_ready(self):
152
204
        """Tell the parent process that the server is ready for writes."""
181
233
        os.kill(self.pid, SIGKILL)
182
234
 
183
235
 
 
236
SUPPORTED_DISPLAY_TYPES = set(['all', 'text-only', 'headers-only'])
 
237
 
 
238
 
184
239
SUPPORTED_ORDERS = set(
185
240
    ['date', 'author', 'subject', 'thread_newest', 'thread_oldest',
186
241
     'thread_subject'])
217
272
                self.end_headers()
218
273
                self.wfile.write(simplejson.dumps(response))
219
274
            except UnsupportedOrder:
220
 
                self.send_response(httplib.BAD_REQUEST)
221
 
                self.wfile.write('Unsupported order')
 
275
                self.send_response(
 
276
                    httplib.BAD_REQUEST, UnsupportedOrder.__doc__)
 
277
                return
 
278
            except UnsupportedDisplayType:
 
279
                self.send_response(
 
280
                    httplib.BAD_REQUEST, UnsupportedDisplayType.__doc__)
222
281
                return
223
282
 
224
283
    def log_message(self, format, *args):
232
291
 
233
292
    def test_put_message(self):
234
293
        client = GrackleClient('localhost', 8436)
235
 
        with ForkedFake.from_client(client):
 
294
        with ForkedFakeService.from_client(client):
236
295
            client.put_message('arch1', 'asdf', StringIO('This is a message'))
237
296
            with ExpectedException(Exception, 'wtf'):
238
297
                client.put_message('arch1', 'asdf',
250
309
 
251
310
    def test_get_messages(self):
252
311
        client = GrackleClient('localhost', 8435)
253
 
        with ForkedFake.from_client(client,
254
 
            {'baz':
255
 
            [{'message_id': 'foo'},
256
 
             {'message_id': 'bar'}]}):
 
312
        archive = {
 
313
            'baz': [make_message('foo'), make_message('bar')]}
 
314
        with ForkedFakeService.from_client(client, archive):
257
315
            response = client.get_messages('baz')
258
316
        self.assertEqual(['bar', 'foo'], sorted(m['message_id'] for m in
259
317
            response['messages']))
262
320
 
263
321
    def test_get_messages_by_id(self):
264
322
        client = GrackleClient('localhost', 8437)
265
 
        with ForkedFake.from_client(client,
266
 
            {'baz':
267
 
            [{'message_id': 'foo'},
268
 
             {'message_id': 'bar'}]}):
 
323
        archive = {
 
324
            'baz': [make_message('foo'), make_message('bar')]}
 
325
        with ForkedFakeService.from_client(client, archive):
269
326
            response = client.get_messages('baz', message_ids=['foo'])
270
327
        message, = response['messages']
271
328
        self.assertEqual('foo', message['message_id'])
272
329
 
273
330
    def test_get_messages_batching(self):
274
331
        client = GrackleClient('localhost', 8438)
275
 
        with ForkedFake.from_client(client,
276
 
            {'baz':
277
 
            [{'message_id': 'foo'},
278
 
             {'message_id': 'bar'}]}):
 
332
        archive = {'baz': [make_message('foo'), make_message('bar')]}
 
333
        with ForkedFakeService.from_client(client, archive):
279
334
            response = client.get_messages('baz', limit=1)
280
335
            self.assertEqual(1, len(response['messages']))
281
336
            messages = response['messages']
287
342
 
288
343
    def get_messages_member_order_test(self, key):
289
344
        client = GrackleClient('localhost', 8439)
290
 
        with ForkedFake.from_client(client,
291
 
                {'baz': [{'message_id': 'foo', key: '2011-03-25'},
292
 
                 {'message_id': 'bar', key: '2011-03-24'}]}):
 
345
        archive = {
 
346
            'baz': [
 
347
                make_message('foo', headers={key: '2011-03-25'}),
 
348
                make_message('bar', headers={key: '2011-03-24'}),
 
349
             ]}
 
350
        with ForkedFakeService.from_client(client, archive):
293
351
            response = client.get_messages('baz')
294
352
            self.assertIDOrder(['foo', 'bar'], response['messages'])
295
353
            response = client.get_messages('baz', order=key)
305
363
        self.get_messages_member_order_test('subject')
306
364
 
307
365
    def test_get_messages_thread_subject_order(self):
 
366
        archive = {
 
367
            'baz': [
 
368
                make_message('bar', headers={'subject': 'y'}),
 
369
                make_message('qux', headers={'subject': 'z'}),
 
370
                make_message('foo', headers={'subject': 'x',
 
371
                                             'in-reply-to': 'qux'}),
 
372
             ]}
308
373
        client = GrackleClient('localhost', 8439)
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
 
            ]}):
 
374
        with ForkedFakeService.from_client(client, archive):
314
375
            response = client.get_messages('baz')
315
376
            self.assertIDOrder(['bar', 'qux', 'foo'], response['messages'])
316
377
            response = client.get_messages('baz', order='subject')
320
381
 
321
382
    def test_get_messages_thread_oldest_order(self):
322
383
        client = GrackleClient('localhost', 8439)
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
 
            ]}):
 
384
        archive = {
 
385
            'baz': [
 
386
                make_message('bar', headers={'date': 'x'}),
 
387
                make_message('qux', headers={'date': 'z'}),
 
388
                make_message('foo', headers={'date': 'y',
 
389
                                             'in-reply-to': 'qux'}),
 
390
            ]}
 
391
        with ForkedFakeService.from_client(client, archive):
328
392
            response = client.get_messages('baz')
329
393
            self.assertIDOrder(['bar', 'qux', 'foo'], response['messages'])
330
394
            response = client.get_messages('baz', order='date')
334
398
 
335
399
    def test_get_messages_thread_newest_order(self):
336
400
        client = GrackleClient('localhost', 8439)
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
 
            ]}):
 
401
        archive = {
 
402
            'baz': [
 
403
                make_message('bar', headers={'date': 'x'}),
 
404
                make_message('qux', headers={'date': 'w'}),
 
405
                make_message('foo', headers={'date': 'y',
 
406
                                             'in-reply-to': 'bar'}),
 
407
                make_message('baz', headers={'date': 'z',
 
408
                                             'in-reply-to': 'qux'}),
 
409
            ]}
 
410
        with ForkedFakeService.from_client(client, archive):
343
411
            response = client.get_messages('baz', order='date')
344
412
            self.assertIDOrder(
345
413
                ['qux', 'bar', 'foo', 'baz'], response['messages'])
349
417
 
350
418
    def test_get_messages_unsupported_order(self):
351
419
        client = GrackleClient('localhost', 8439)
352
 
        with ForkedFake.from_client(client,
353
 
                {'baz': [{'message_id': 'foo', 'date': '2011-03-25'},
354
 
                 {'message_id': 'bar', 'date': '2011-03-24'}]}):
 
420
        archive = {
 
421
            'baz': [
 
422
                make_message('foo', headers={'date': '2011-03-25'}),
 
423
                make_message('foo', headers={'date': '2011-03-24'}),
 
424
            ]}
 
425
        with ForkedFakeService.from_client(client, archive):
355
426
            with ExpectedException(UnsupportedOrder, ''):
356
427
                client.get_messages('baz', order='nonsense')
357
428
 
358
429
    def test_get_messages_headers_no_headers(self):
359
430
        client = GrackleClient('localhost', 8440)
360
 
        with ForkedFake.from_client(client,
361
 
            {'baz': [
362
 
                {'message_id': 'foo'}
363
 
            ]}):
 
431
        archive = {'baz': [make_message('foo')]}
 
432
        with ForkedFakeService.from_client(client, archive):
364
433
            response = client.get_messages('baz', headers=[
365
434
                'Subject', 'Date', 'X-Launchpad-Message-Rationale'])
366
435
        first_message = response['messages'][0]
369
438
 
370
439
    def test_get_messages_headers_exclude_headers(self):
371
440
        client = GrackleClient('localhost', 8441)
372
 
        with ForkedFake.from_client(client,
373
 
            {'baz': [
374
 
                {'message_id': 'foo', 'headers': {'From': 'me'}}
375
 
            ]}):
 
441
        archive = {
 
442
            'baz': [make_message('foo', headers={'From': 'me'})]}
 
443
        with ForkedFakeService.from_client(client, archive):
376
444
            response = client.get_messages('baz', headers=[
377
445
                'Subject', 'Date', 'X-Launchpad-Message-Rationale'])
378
446
        first_message = response['messages'][0]
381
449
 
382
450
    def test_get_messages_headers_include_headers(self):
383
451
        client = GrackleClient('localhost', 8442)
384
 
        with ForkedFake.from_client(client,
385
 
            {'baz': [
386
 
                {'message_id': 'foo', 'headers': {'From': 'me', 'To': 'you'}}
387
 
            ]}):
 
452
        archive = {
 
453
            'baz': [
 
454
                make_message('foo', headers={'From': 'me', 'To': 'you'})]}
 
455
        with ForkedFakeService.from_client(client, archive):
388
456
            response = client.get_messages('baz', headers=[
389
457
                'From', 'To'])
390
458
        first_message = response['messages'][0]
393
461
 
394
462
    def test_get_messages_max_body_length(self):
395
463
        client = GrackleClient('localhost', 8443)
396
 
        with ForkedFake.from_client(client,
397
 
            {'baz': [
398
 
                {'message_id': 'foo', 'body': u'abcdefghi'}
399
 
            ]}):
 
464
        archive = {'baz': [make_message('foo', body=u'abcdefghi')]}
 
465
        with ForkedFakeService.from_client(client, archive):
400
466
            response = client.get_messages('baz', max_body_length=3)
401
467
        first_message = response['messages'][0]
402
468
        self.assertEqual('abc', first_message['body'])
403
469
 
404
470
    def test_include_hidden(self):
405
471
        client = GrackleClient('localhost', 8444)
406
 
        with ForkedFake.from_client(client,
407
 
            {'baz': [
408
 
                {'message_id': 'foo', 'hidden': True},
409
 
                {'message_id': 'bar', 'hidden': False}
410
 
            ]}):
 
472
        archive = {
 
473
            'baz': [
 
474
                make_message('foo', hidden=True),
 
475
                make_message('bar', hidden=False),
 
476
            ]}
 
477
        with ForkedFakeService.from_client(client, archive):
411
478
            response = client.get_messages('baz', include_hidden=True)
412
479
            self.assertMessageIDs(['bar', 'foo'], response['messages'])
413
480
            response = client.get_messages('baz', include_hidden=False)
414
481
            self.assertMessageIDs(['bar'], response['messages'])
 
482
 
 
483
    def test_display_type_unknown_value(self):
 
484
        client = GrackleClient('localhost', 8445)
 
485
        archive = {'baz': [make_message('foo', body=u'abcdefghi')]}
 
486
        with ForkedFakeService.from_client(client, archive):
 
487
            with ExpectedException(UnsupportedDisplayType, ''):
 
488
                client.get_messages('baz', display_type='unknown')
 
489
 
 
490
    def test_display_type_headers_only(self):
 
491
        client = GrackleClient('localhost', 8446)
 
492
        archive = {
 
493
            'baz': [
 
494
                make_message('foo', body=u'abcdefghi',
 
495
                             headers={'From': 'me', 'To': 'you'})]}
 
496
        with ForkedFakeService.from_client(client, archive):
 
497
            response = client.get_messages('baz', display_type='headers-only')
 
498
        first_message = response['messages'][0]
 
499
        self.assertEqual('foo', first_message['message_id'])
 
500
        self.assertEqual(
 
501
            {'From': 'me', 'Message-Id': 'foo', 'To': 'you'},
 
502
            first_message['headers'])
 
503
        self.assertNotIn('body', first_message)
 
504
 
 
505
    def test_display_type_text_only(self):
 
506
        client = GrackleClient('localhost', 8446)
 
507
        archive = {
 
508
            'baz': [
 
509
                make_mime_message(
 
510
                    'foo', 'abcdefghi',
 
511
                    headers={'From': 'me', 'To': 'you'},
 
512
                    attachment_type='text/x-diff')]}
 
513
        with ForkedFakeService.from_client(client, archive):
 
514
            response = client.get_messages('baz', display_type='text-only')
 
515
        first_message = response['messages'][0]
 
516
        self.assertEqual('foo', first_message['message_id'])
 
517
        self.assertEqual('me', first_message['headers']['From'])
 
518
        self.assertEqual('you', first_message['headers']['To'])
 
519
        self.assertEqual('abcdefghi', first_message['body'])
 
520
 
 
521
    def test_display_type_all(self):
 
522
        client = GrackleClient('localhost', 8447)
 
523
        archive = {
 
524
            'baz': [
 
525
                make_mime_message(
 
526
                    'foo', 'abcdefghi',
 
527
                    headers={'From': 'me', 'To': 'you'},
 
528
                    attachment_type='text/x-diff')]}
 
529
        with ForkedFakeService.from_client(client, archive):
 
530
            response = client.get_messages('baz', display_type='all')
 
531
        first_message = response['messages'][0]
 
532
        self.assertEqual('foo', first_message['message_id'])
 
533
        self.assertEqual('me', first_message['headers']['From'])
 
534
        self.assertEqual('you', first_message['headers']['To'])
 
535
        self.assertEqual(
 
536
            'abcdefghi\n\nattactment data.', first_message['body'])