~didrocks/unity/altf10

« back to all changes in this revision

Viewing changes to grackle/tests/test_client.py

  • Committer: Curtis Hovey
  • Date: 2012-01-30 18:31:19 UTC
  • mfrom: (35.1.2 client-get-messages-0)
  • Revision ID: curtis.hovey@canonical.com-20120130183119-ylmo65mur5ogktud
Merged lint fixes.

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