21
18
from grackle.client import (
24
UnsupportedDisplayType,
29
def make_message(message_id, body='body', headers=None, hidden=False):
32
headers['Message-Id'] = message_id
34
'message_id': message_id,
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'),
42
'replies': headers.get('in-reply-to', None),
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)
61
24
def threaded_messages(messages):
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]
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()
76
39
class GrackleStore:
77
"""A memory-backed message store."""
79
41
def __init__(self, messages):
81
42
self.messages = messages
84
def is_multipart(message):
85
return isinstance(message['body'], list)
87
44
def get_messages(self, archive_id, query_string):
88
"""Return matching messages.
90
:param archive_id: The archive to retrieve from.
91
:param query_string: Contains 'parameters', which is a JSON-format
92
string describing parameters.
94
45
query = parse_qs(query_string)
95
46
parameters = simplejson.loads(query['parameters'][0])
96
47
order = parameters.get('order')
97
48
messages = self.messages[archive_id]
49
if order is not None :
99
50
if order not in SUPPORTED_ORDERS:
100
51
raise UnsupportedOrder
101
52
elif order.startswith('thread_'):
329
231
def test_get_messages_by_id(self):
330
232
client = GrackleClient('localhost', 8437)
332
'baz': [make_message('foo'), make_message('bar')]}
333
with ForkedFakeService.from_client(client, archive):
233
with ForkedFake.from_client(client,
235
[{'message_id': 'foo'},
236
{'message_id': 'bar'}]}):
334
237
response = client.get_messages('baz', message_ids=['foo'])
335
238
message, = response['messages']
336
239
self.assertEqual('foo', message['message_id'])
338
241
def test_get_messages_batching(self):
339
242
client = GrackleClient('localhost', 8438)
340
archive = {'baz': [make_message('foo'), make_message('bar')]}
341
with ForkedFakeService.from_client(client, archive):
243
with ForkedFake.from_client(client,
245
[{'message_id': 'foo'},
246
{'message_id': 'bar'}]}):
342
247
response = client.get_messages('baz', limit=1)
343
248
self.assertEqual(1, len(response['messages']))
344
249
messages = response['messages']
407
303
def test_get_messages_thread_newest_order(self):
408
304
client = GrackleClient('localhost', 8439)
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'}),
418
with ForkedFakeService.from_client(client, archive):
305
with ForkedFake.from_client(client, {'baz': [
306
{'message_id': 'bar', 'date': 'x'},
307
{'message_id': 'qux', 'date': 'w'},
308
{'message_id': 'foo', 'date': 'y', 'in_reply_to': 'bar'},
309
{'message_id': 'baz', 'date': 'z', 'in_reply_to': 'qux'},
419
311
response = client.get_messages('baz', order='date')
420
312
self.assertIDOrder(
421
313
['qux', 'bar', 'foo', 'baz'], response['messages'])
426
318
def test_get_messages_unsupported_order(self):
427
319
client = GrackleClient('localhost', 8439)
430
make_message('foo', headers={'date': '2011-03-25'}),
431
make_message('foo', headers={'date': '2011-03-24'}),
433
with ForkedFakeService.from_client(client, archive):
434
with ExpectedException(UnsupportedOrder, ''):
320
with ForkedFake.from_client(client,
321
{'baz': [{'message_id': 'foo', 'date': '2011-03-25'},
322
{'message_id': 'bar', 'date': '2011-03-24'}]}):
323
with ExpectedException(UnsupportedOrder):
435
324
client.get_messages('baz', order='nonsense')
437
326
def test_get_messages_headers_no_headers(self):
438
327
client = GrackleClient('localhost', 8440)
439
archive = {'baz': [make_message('foo')]}
440
with ForkedFakeService.from_client(client, archive):
328
with ForkedFake.from_client(client,
330
{'message_id': 'foo'}
441
332
response = client.get_messages('baz', headers=[
442
333
'Subject', 'Date', 'X-Launchpad-Message-Rationale'])
443
334
first_message = response['messages'][0]
470
362
def test_get_messages_max_body_length(self):
471
363
client = GrackleClient('localhost', 8443)
472
archive = {'baz': [make_message('foo', body=u'abcdefghi')]}
473
with ForkedFakeService.from_client(client, archive):
364
with ForkedFake.from_client(client,
366
{'message_id': 'foo', 'body': u'abcdefghi'}
474
368
response = client.get_messages('baz', max_body_length=3)
475
369
first_message = response['messages'][0]
476
370
self.assertEqual('abc', first_message['body'])
478
372
def test_include_hidden(self):
479
373
client = GrackleClient('localhost', 8444)
482
make_message('foo', hidden=True),
483
make_message('bar', hidden=False),
485
with ForkedFakeService.from_client(client, archive):
374
with ForkedFake.from_client(client,
376
{'message_id': 'foo', 'hidden': True},
377
{'message_id': 'bar', 'hidden': False}
486
379
response = client.get_messages('baz', include_hidden=True)
487
380
self.assertMessageIDs(['bar', 'foo'], response['messages'])
488
381
response = client.get_messages('baz', include_hidden=False)
489
382
self.assertMessageIDs(['bar'], response['messages'])
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')
498
def test_display_type_headers_only(self):
499
client = GrackleClient('localhost', 8446)
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'])
509
{'From': 'me', 'Message-Id': 'foo', 'To': 'you'},
510
first_message['headers'])
511
self.assertNotIn('body', first_message)
513
def test_display_type_text_only(self):
514
client = GrackleClient('localhost', 8446)
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'])
529
def test_display_type_all(self):
530
client = GrackleClient('localhost', 8447)
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'])
544
'abcdefghi\n\nattactment data.', first_message['body'])
546
def test_date_range(self):
547
client = GrackleClient('localhost', 8448)
551
'foo', 'abcdefghi', headers={'date': '2011-12-31'}),
553
'bar', 'abcdefghi', headers={'date': '2012-01-01'}),
555
'qux', 'abcdefghi', headers={'date': '2012-01-15'}),
557
'naf', 'abcdefghi', headers={'date': '2012-01-31'}),
559
'doh', 'abcdefghi', headers={'date': '2012-02-01'}),
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)
567
def test_date_range_unparsabledaterange(self):
568
client = GrackleClient('localhost', 8449)
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')
574
def test_date_range_unparsabledaterange_missing_part(self):
575
client = GrackleClient('localhost', 8450)
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..')
581
def test_date_range_unparsabledaterange_extra_part(self):
582
client = GrackleClient('localhost', 8451)
583
archive = {'baz': [make_message('foo', body=u'abcdefghi')]}
584
with ForkedFakeService.from_client(client, archive):
585
with ExpectedException(UnparsableDateRange, ''):
586
client.get_messages('baz', date_range='2012-01..12-02..12-03')