~didrocks/unity/altf10

« back to all changes in this revision

Viewing changes to grackle/tests/test_client.py

  • Committer: Curtis Hovey
  • Date: 2012-02-14 22:52:55 UTC
  • Revision ID: curtis.hovey@canonical.com-20120214225255-7cwc33qlpmaztebo
Dates must be a value.

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