3
3
BaseHTTPRequestHandler,
5
from email.message import Message
6
from email.mime.multipart import MIMEMultipart
7
from email.mime.text import MIMEText
11
7
from signal import SIGKILL
13
9
from StringIO import StringIO
15
10
from unittest import TestCase
16
11
from urlparse import urlparse
12
from urlparse import parse_qs
18
14
from testtools import ExpectedException
20
16
from grackle.client import (
23
UnsupportedDisplayType,
26
from grackle.store import (
31
def make_message(message_id, body='body', headers=None, hidden=False):
34
headers['Message-Id'] = message_id
36
'message_id': message_id,
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'),
44
'replies': headers.get('in-reply-to', None),
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)
63
class ForkedFakeService:
64
"""A Grackle service fake, as a ContextManager."""
66
def __init__(self, port, message_archives=None, write_logs=False):
69
:param port: The tcp port to use.
70
:param message_archives: A dict of lists of dicts representing
71
archives of messages. The outer dict represents the archive,
72
the list represents the list of messages for that archive.
73
:param write_logs: If true, log messages will be written to stdout.
23
def __init__(self, port, messages=None):
77
if message_archives is None:
78
self.message_archives = {}
80
self.message_archives = message_archives
26
self.messages = messages
81
27
self.read_end, self.write_end = os.pipe()
82
self.write_logs = write_logs
85
def from_client(client, message_archives=None):
86
"""Instantiate a ForkedFakeService from the client.
88
:param port: The client to provide service for.
89
:param message_archives: A dict of lists of dicts representing
90
archives of messages. The outer dict represents the archive,
91
the list represents the list of messages for that archive.
93
return ForkedFakeService(client.port, message_archives)
95
29
def is_ready(self):
96
"""Tell the parent process that the server is ready for writes."""
97
30
os.write(self.write_end, 'asdf')
99
32
def __enter__(self):
102
Fork and start a server in the child. Return when the server is ready
106
35
self.start_server()
201
111
def test_get_messages_by_id(self):
202
112
client = GrackleClient('localhost', 8437)
204
'baz': [make_message('foo'), make_message('bar')]}
205
with ForkedFakeService.from_client(client, archive):
113
with fake_grackle_service(client,
115
[{'message_id': 'foo'},
116
{'message_id': 'bar'}]}):
206
117
response = client.get_messages('baz', message_ids=['foo'])
207
118
message, = response['messages']
208
119
self.assertEqual('foo', message['message_id'])
210
def test_get_messages_batching(self):
211
client = GrackleClient('localhost', 8438)
212
archive = {'baz': [make_message('foo'), make_message('bar')]}
213
with ForkedFakeService.from_client(client, archive):
214
response = client.get_messages('baz', limit=1)
215
self.assertEqual(1, len(response['messages']))
216
messages = response['messages']
217
response = client.get_messages(
218
'baz', limit=1, memo=response['next_memo'])
219
self.assertEqual(1, len(response['messages']))
220
messages.extend(response['messages'])
221
self.assertMessageIDs(['foo', 'bar'], messages)
223
def get_messages_member_order_test(self, key):
224
client = GrackleClient('localhost', 8439)
227
make_message('foo', headers={key: '2011-03-25'}),
228
make_message('bar', headers={key: '2011-03-24'}),
230
with ForkedFakeService.from_client(client, archive):
231
response = client.get_messages('baz')
232
self.assertIDOrder(['foo', 'bar'], response['messages'])
233
response = client.get_messages('baz', order=key)
234
self.assertIDOrder(['bar', 'foo'], response['messages'])
236
def test_get_messages_date_order(self):
237
self.get_messages_member_order_test('date')
239
def test_get_messages_author_order(self):
240
self.get_messages_member_order_test('author')
242
def test_get_messages_subject_order(self):
243
self.get_messages_member_order_test('subject')
245
def test_get_messages_thread_subject_order(self):
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'}),
253
client = GrackleClient('localhost', 8439)
254
with ForkedFakeService.from_client(client, archive):
255
response = client.get_messages('baz')
256
self.assertIDOrder(['bar', 'qux', 'foo'], response['messages'])
257
response = client.get_messages('baz', order='subject')
258
self.assertIDOrder(['foo', 'bar', 'qux'], response['messages'])
259
response = client.get_messages('baz', order='thread_subject')
260
self.assertIDOrder(['bar', 'qux', 'foo'], response['messages'])
262
def test_get_messages_thread_oldest_order(self):
263
client = GrackleClient('localhost', 8439)
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'}),
271
with ForkedFakeService.from_client(client, archive):
272
response = client.get_messages('baz')
273
self.assertIDOrder(['bar', 'qux', 'foo'], response['messages'])
274
response = client.get_messages('baz', order='date')
275
self.assertIDOrder(['bar', 'foo', 'qux'], response['messages'])
276
response = client.get_messages('baz', order='thread_oldest')
277
self.assertIDOrder(['bar', 'qux', 'foo'], response['messages'])
279
def test_get_messages_thread_newest_order(self):
280
client = GrackleClient('localhost', 8439)
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'}),
290
with ForkedFakeService.from_client(client, archive):
291
response = client.get_messages('baz', order='date')
293
['qux', 'bar', 'foo', 'baz'], response['messages'])
294
response = client.get_messages('baz', order='thread_newest')
296
['bar', 'foo', 'qux', 'baz'], response['messages'])
298
def test_get_messages_unsupported_order(self):
299
client = GrackleClient('localhost', 8439)
302
make_message('foo', headers={'date': '2011-03-25'}),
303
make_message('foo', headers={'date': '2011-03-24'}),
305
with ForkedFakeService.from_client(client, archive):
306
with ExpectedException(UnsupportedOrder, ''):
307
client.get_messages('baz', order='nonsense')
309
def test_get_messages_headers_no_headers(self):
310
client = GrackleClient('localhost', 8440)
311
archive = {'baz': [make_message('foo')]}
312
with ForkedFakeService.from_client(client, archive):
313
response = client.get_messages('baz', headers=[
314
'Subject', 'Date', 'X-Launchpad-Message-Rationale'])
315
first_message = response['messages'][0]
316
self.assertEqual('foo', first_message['message_id'])
317
self.assertEqual({}, first_message['headers'])
319
def test_get_messages_headers_exclude_headers(self):
320
client = GrackleClient('localhost', 8441)
322
'baz': [make_message('foo', headers={'From': 'me'})]}
323
with ForkedFakeService.from_client(client, archive):
324
response = client.get_messages('baz', headers=[
325
'Subject', 'Date', 'X-Launchpad-Message-Rationale'])
326
first_message = response['messages'][0]
327
self.assertEqual('foo', first_message['message_id'])
328
self.assertEqual({}, first_message['headers'])
330
def test_get_messages_headers_include_headers(self):
331
client = GrackleClient('localhost', 8442)
334
make_message('foo', headers={'From': 'me', 'To': 'you'})]}
335
with ForkedFakeService.from_client(client, archive):
336
response = client.get_messages('baz', headers=[
338
first_message = response['messages'][0]
339
self.assertEqual('foo', first_message['message_id'])
340
self.assertEqual({'From': 'me', 'To': 'you'}, first_message['headers'])
342
def test_get_messages_max_body_length(self):
343
client = GrackleClient('localhost', 8443)
344
archive = {'baz': [make_message('foo', body=u'abcdefghi')]}
345
with ForkedFakeService.from_client(client, archive):
346
response = client.get_messages('baz', max_body_length=3)
347
first_message = response['messages'][0]
348
self.assertEqual('abc', first_message['body'])
350
def test_include_hidden(self):
351
client = GrackleClient('localhost', 8444)
354
make_message('foo', hidden=True),
355
make_message('bar', hidden=False),
357
with ForkedFakeService.from_client(client, archive):
358
response = client.get_messages('baz', include_hidden=True)
359
self.assertMessageIDs(['bar', 'foo'], response['messages'])
360
response = client.get_messages('baz', include_hidden=False)
361
self.assertMessageIDs(['bar'], response['messages'])
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')
370
def test_display_type_headers_only(self):
371
client = GrackleClient('localhost', 8446)
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'])
381
{'From': 'me', 'Message-Id': 'foo', 'To': 'you'},
382
first_message['headers'])
383
self.assertNotIn('body', first_message)
385
def test_display_type_text_only(self):
386
client = GrackleClient('localhost', 8446)
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'])
401
def test_display_type_all(self):
402
client = GrackleClient('localhost', 8447)
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'])
416
'abcdefghi\n\nattactment data.', first_message['body'])
418
def test_date_range(self):
419
client = GrackleClient('localhost', 8448)
423
'foo', 'abcdefghi', headers={'date': '2011-12-31'}),
425
'bar', 'abcdefghi', headers={'date': '2012-01-01'}),
427
'qux', 'abcdefghi', headers={'date': '2012-01-15'}),
429
'naf', 'abcdefghi', headers={'date': '2012-01-31'}),
431
'doh', 'abcdefghi', headers={'date': '2012-02-01'}),
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)
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')
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..')
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')