~didrocks/unity/altf10

« back to all changes in this revision

Viewing changes to grackle/tests/test_client.py

  • Committer: Curtis Hovey
  • Date: 2012-02-24 21:34:16 UTC
  • Revision ID: curtis.hovey@canonical.com-20120224213416-pao8llb7wwyy15bb
Removed untested and defintitely broken-by-design method.

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
11
14
import sys
12
15
from unittest import TestCase
13
16
from urlparse import urlparse
14
 
from urlparse import parse_qs
15
17
 
16
18
from testtools import ExpectedException
17
19
 
18
20
from grackle.client import (
19
21
    GrackleClient,
 
22
    UnparsableDateRange,
 
23
    UnsupportedDisplayType,
20
24
    UnsupportedOrder,
21
25
    )
22
 
 
23
 
 
24
 
def threaded_messages(messages):
25
 
    threads = {}
26
 
    count = 0
27
 
    pending = []
28
 
    for message in messages:
29
 
        if message.get('in_reply_to') is None:
30
 
            threads[message['message_id']] = [message]
31
 
            count += 1
32
 
        else:
33
 
            pending.append(message)
34
 
    for message in pending:
35
 
        threads[message['in_reply_to']].append(message)
36
 
    return threads.values()
37
 
 
38
 
 
39
 
class GrackleStore:
40
 
    """A memory-backed message store."""
41
 
 
42
 
    def __init__(self, messages):
43
 
        """Constructor."""
44
 
        self.messages = messages
45
 
 
46
 
    def get_messages(self, archive_id, query_string):
47
 
        """Return matching messages.
48
 
 
49
 
        :param archive_id: The archive to retrieve from.
50
 
        :param query_string: Contains 'parameters', which is a JSON-format
51
 
            string describing parameters.
52
 
        """
53
 
        query = parse_qs(query_string)
54
 
        parameters = simplejson.loads(query['parameters'][0])
55
 
        order = parameters.get('order')
56
 
        messages = self.messages[archive_id]
57
 
        if order is not None:
58
 
            if order not in SUPPORTED_ORDERS:
59
 
                raise UnsupportedOrder
60
 
            elif order.startswith('thread_'):
61
 
                threaded = threaded_messages(messages)
62
 
                messages = []
63
 
                if order == 'thread_subject':
64
 
                    threaded.sort(key=lambda t: t[0]['subject'])
65
 
                if order == 'thread_oldest':
66
 
                    threaded.sort(key=lambda t: min(m['date'] for m in t))
67
 
                if order == 'thread_newest':
68
 
                    threaded.sort(key=lambda t: max(m['date'] for m in t))
69
 
                for thread in threaded:
70
 
                    messages.extend(thread)
71
 
            else:
72
 
                messages.sort(key=lambda m: m[order])
73
 
        new_messages = []
74
 
        for message in messages:
75
 
            if (not parameters['include_hidden']
76
 
                and message.get('hidden', False)):
77
 
                continue
78
 
 
79
 
            if ('message_ids' in parameters
80
 
                and message['message_id'] not in parameters['message_ids']):
81
 
                continue
82
 
            message = dict(message)
83
 
            if 'headers' in parameters:
84
 
                headers = dict(
85
 
                    (k, v) for k, v in message['headers'].iteritems()
86
 
                    if k in parameters['headers'])
87
 
                message['headers'] = headers
88
 
            max_body = parameters.get('max_body_length')
89
 
            if max_body is not None:
90
 
                message['body'] = message['body'][:max_body]
91
 
            new_messages.append(message)
92
 
        messages = new_messages
93
 
        limit = parameters.get('limit', 100)
94
 
        memo = parameters.get('memo')
95
 
        message_id_indices = dict(
96
 
            (m['message_id'], idx) for idx, m in enumerate(messages))
97
 
        if memo is None:
98
 
            start = 0
99
 
        else:
100
 
            start = message_id_indices[memo.encode('rot13')]
101
 
        if start > 0:
102
 
            previous_memo = messages[start - 1]['message_id'].encode('rot13')
103
 
        else:
104
 
            previous_memo = None
105
 
        end = min(start + limit, len(messages))
106
 
        if end < len(messages):
107
 
            next_memo = messages[end]['message_id'].encode('rot13')
108
 
        else:
109
 
            next_memo = None
110
 
        messages = messages[start:end]
111
 
 
112
 
        response = {
113
 
            'messages': messages,
114
 
            'next_memo': next_memo,
115
 
            'previous_memo': previous_memo
116
 
            }
117
 
        return response
 
26
from grackle.store import (
 
27
    MemoryStore,
 
28
    )
 
29
 
 
30
 
 
31
def make_message(message_id, body='body', headers=None, hidden=False):
 
32
    if headers is None:
 
33
        headers = {}
 
34
    headers['Message-Id'] = message_id
 
35
    message = {
 
36
        'message_id': message_id,
 
37
        'headers': headers,
 
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'),
 
42
        'hidden': hidden,
 
43
        'attachments': [],
 
44
        'replies': headers.get('in-reply-to', None),
 
45
        'body': body,
 
46
        }
 
47
    return message
 
48
 
 
49
 
 
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)
118
61
 
119
62
 
120
63
class ForkedFakeService:
168
111
    def start_server(self):
169
112
        """Start the HTTP server."""
170
113
        service = HTTPServer(('', self.port), FakeGrackleRequestHandler)
171
 
        service.store = GrackleStore(self.messages)
 
114
        service.store = MemoryStore(self.messages)
172
115
        for archive_id, messages in service.store.messages.iteritems():
173
116
            for message in messages:
174
117
                message.setdefault('headers', {})
182
125
        os.kill(self.pid, SIGKILL)
183
126
 
184
127
 
185
 
SUPPORTED_ORDERS = set(
186
 
    ['date', 'author', 'subject', 'thread_newest', 'thread_oldest',
187
 
     'thread_subject'])
188
 
 
189
 
 
190
128
class FakeGrackleRequestHandler(BaseHTTPRequestHandler):
191
129
    """A request handler that forwards to server.store."""
192
130
 
217
155
                self.send_response(httplib.OK)
218
156
                self.end_headers()
219
157
                self.wfile.write(simplejson.dumps(response))
220
 
            except UnsupportedOrder:
221
 
                self.send_response(httplib.BAD_REQUEST)
222
 
                self.wfile.write('Unsupported order')
 
158
            except Exception, error:
 
159
                self.send_response(
 
160
                    httplib.BAD_REQUEST, error.__doc__)
223
161
                return
224
162
 
225
163
    def log_message(self, format, *args):
232
170
class TestPutMessage(TestCase):
233
171
 
234
172
    def test_put_message(self):
235
 
        client = GrackleClient('localhost', 8436)
 
173
        client = GrackleClient('localhost', 8420)
236
174
        with ForkedFakeService.from_client(client):
237
175
            client.put_message('arch1', 'asdf', StringIO('This is a message'))
238
176
            with ExpectedException(Exception, 'wtf'):
250
188
            sorted(ids), sorted(messages, key=lambda m: m['message_id']))
251
189
 
252
190
    def test_get_messages(self):
253
 
        client = GrackleClient('localhost', 8435)
254
 
        with ForkedFakeService.from_client(client,
255
 
            {'baz':
256
 
            [{'message_id': 'foo'},
257
 
             {'message_id': 'bar'}]}):
 
191
        client = GrackleClient('localhost', 8430)
 
192
        archive = {
 
193
            'baz': [make_message('foo'), make_message('bar')]}
 
194
        with ForkedFakeService.from_client(client, archive):
258
195
            response = client.get_messages('baz')
259
196
        self.assertEqual(['bar', 'foo'], sorted(m['message_id'] for m in
260
197
            response['messages']))
263
200
 
264
201
    def test_get_messages_by_id(self):
265
202
        client = GrackleClient('localhost', 8437)
266
 
        with ForkedFakeService.from_client(client,
267
 
            {'baz':
268
 
            [{'message_id': 'foo'},
269
 
             {'message_id': 'bar'}]}):
 
203
        archive = {
 
204
            'baz': [make_message('foo'), make_message('bar')]}
 
205
        with ForkedFakeService.from_client(client, archive):
270
206
            response = client.get_messages('baz', message_ids=['foo'])
271
207
        message, = response['messages']
272
208
        self.assertEqual('foo', message['message_id'])
273
209
 
274
210
    def test_get_messages_batching(self):
275
211
        client = GrackleClient('localhost', 8438)
276
 
        with ForkedFakeService.from_client(client,
277
 
            {'baz':
278
 
            [{'message_id': 'foo'},
279
 
             {'message_id': 'bar'}]}):
 
212
        archive = {'baz': [make_message('foo'), make_message('bar')]}
 
213
        with ForkedFakeService.from_client(client, archive):
280
214
            response = client.get_messages('baz', limit=1)
281
215
            self.assertEqual(1, len(response['messages']))
282
216
            messages = response['messages']
288
222
 
289
223
    def get_messages_member_order_test(self, key):
290
224
        client = GrackleClient('localhost', 8439)
291
 
        with ForkedFakeService.from_client(client,
292
 
                {'baz': [{'message_id': 'foo', key: '2011-03-25'},
293
 
                 {'message_id': 'bar', key: '2011-03-24'}]}):
 
225
        archive = {
 
226
            'baz': [
 
227
                make_message('foo', headers={key: '2011-03-25'}),
 
228
                make_message('bar', headers={key: '2011-03-24'}),
 
229
             ]}
 
230
        with ForkedFakeService.from_client(client, archive):
294
231
            response = client.get_messages('baz')
295
232
            self.assertIDOrder(['foo', 'bar'], response['messages'])
296
233
            response = client.get_messages('baz', order=key)
306
243
        self.get_messages_member_order_test('subject')
307
244
 
308
245
    def test_get_messages_thread_subject_order(self):
 
246
        archive = {
 
247
            'baz': [
 
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'}),
 
252
             ]}
309
253
        client = GrackleClient('localhost', 8439)
310
 
        with ForkedFakeService.from_client(client, {'baz': [
311
 
            {'message_id': 'bar', 'subject': 'y'},
312
 
            {'message_id': 'qux', 'subject': 'z'},
313
 
            {'message_id': 'foo', 'subject': 'x', 'in_reply_to': 'qux'},
314
 
            ]}):
 
254
        with ForkedFakeService.from_client(client, archive):
315
255
            response = client.get_messages('baz')
316
256
            self.assertIDOrder(['bar', 'qux', 'foo'], response['messages'])
317
257
            response = client.get_messages('baz', order='subject')
321
261
 
322
262
    def test_get_messages_thread_oldest_order(self):
323
263
        client = GrackleClient('localhost', 8439)
324
 
        with ForkedFakeService.from_client(client, {'baz': [
325
 
            {'message_id': 'bar', 'date': 'x'},
326
 
            {'message_id': 'qux', 'date': 'z'},
327
 
            {'message_id': 'foo', 'date': 'y', 'in_reply_to': 'qux'},
328
 
            ]}):
 
264
        archive = {
 
265
            'baz': [
 
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'}),
 
270
            ]}
 
271
        with ForkedFakeService.from_client(client, archive):
329
272
            response = client.get_messages('baz')
330
273
            self.assertIDOrder(['bar', 'qux', 'foo'], response['messages'])
331
274
            response = client.get_messages('baz', order='date')
335
278
 
336
279
    def test_get_messages_thread_newest_order(self):
337
280
        client = GrackleClient('localhost', 8439)
338
 
        with ForkedFakeService.from_client(client, {'baz': [
339
 
            {'message_id': 'bar', 'date': 'x'},
340
 
            {'message_id': 'qux', 'date': 'w'},
341
 
            {'message_id': 'foo', 'date': 'y', 'in_reply_to': 'bar'},
342
 
            {'message_id': 'baz', 'date': 'z', 'in_reply_to': 'qux'},
343
 
            ]}):
 
281
        archive = {
 
282
            'baz': [
 
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'}),
 
289
            ]}
 
290
        with ForkedFakeService.from_client(client, archive):
344
291
            response = client.get_messages('baz', order='date')
345
292
            self.assertIDOrder(
346
293
                ['qux', 'bar', 'foo', 'baz'], response['messages'])
350
297
 
351
298
    def test_get_messages_unsupported_order(self):
352
299
        client = GrackleClient('localhost', 8439)
353
 
        with ForkedFakeService.from_client(client,
354
 
                {'baz': [{'message_id': 'foo', 'date': '2011-03-25'},
355
 
                 {'message_id': 'bar', 'date': '2011-03-24'}]}):
 
300
        archive = {
 
301
            'baz': [
 
302
                make_message('foo', headers={'date': '2011-03-25'}),
 
303
                make_message('foo', headers={'date': '2011-03-24'}),
 
304
            ]}
 
305
        with ForkedFakeService.from_client(client, archive):
356
306
            with ExpectedException(UnsupportedOrder, ''):
357
307
                client.get_messages('baz', order='nonsense')
358
308
 
359
309
    def test_get_messages_headers_no_headers(self):
360
310
        client = GrackleClient('localhost', 8440)
361
 
        with ForkedFakeService.from_client(client,
362
 
            {'baz': [
363
 
                {'message_id': 'foo'}
364
 
            ]}):
 
311
        archive = {'baz': [make_message('foo')]}
 
312
        with ForkedFakeService.from_client(client, archive):
365
313
            response = client.get_messages('baz', headers=[
366
314
                'Subject', 'Date', 'X-Launchpad-Message-Rationale'])
367
315
        first_message = response['messages'][0]
370
318
 
371
319
    def test_get_messages_headers_exclude_headers(self):
372
320
        client = GrackleClient('localhost', 8441)
373
 
        with ForkedFakeService.from_client(client,
374
 
            {'baz': [
375
 
                {'message_id': 'foo', 'headers': {'From': 'me'}}
376
 
            ]}):
 
321
        archive = {
 
322
            'baz': [make_message('foo', headers={'From': 'me'})]}
 
323
        with ForkedFakeService.from_client(client, archive):
377
324
            response = client.get_messages('baz', headers=[
378
325
                'Subject', 'Date', 'X-Launchpad-Message-Rationale'])
379
326
        first_message = response['messages'][0]
382
329
 
383
330
    def test_get_messages_headers_include_headers(self):
384
331
        client = GrackleClient('localhost', 8442)
385
 
        with ForkedFakeService.from_client(client,
386
 
            {'baz': [
387
 
                {'message_id': 'foo', 'headers': {'From': 'me', 'To': 'you'}}
388
 
            ]}):
 
332
        archive = {
 
333
            'baz': [
 
334
                make_message('foo', headers={'From': 'me', 'To': 'you'})]}
 
335
        with ForkedFakeService.from_client(client, archive):
389
336
            response = client.get_messages('baz', headers=[
390
337
                'From', 'To'])
391
338
        first_message = response['messages'][0]
394
341
 
395
342
    def test_get_messages_max_body_length(self):
396
343
        client = GrackleClient('localhost', 8443)
397
 
        with ForkedFakeService.from_client(client,
398
 
            {'baz': [
399
 
                {'message_id': 'foo', 'body': u'abcdefghi'}
400
 
            ]}):
 
344
        archive = {'baz': [make_message('foo', body=u'abcdefghi')]}
 
345
        with ForkedFakeService.from_client(client, archive):
401
346
            response = client.get_messages('baz', max_body_length=3)
402
347
        first_message = response['messages'][0]
403
348
        self.assertEqual('abc', first_message['body'])
404
349
 
405
350
    def test_include_hidden(self):
406
351
        client = GrackleClient('localhost', 8444)
407
 
        with ForkedFakeService.from_client(client,
408
 
            {'baz': [
409
 
                {'message_id': 'foo', 'hidden': True},
410
 
                {'message_id': 'bar', 'hidden': False}
411
 
            ]}):
 
352
        archive = {
 
353
            'baz': [
 
354
                make_message('foo', hidden=True),
 
355
                make_message('bar', hidden=False),
 
356
            ]}
 
357
        with ForkedFakeService.from_client(client, archive):
412
358
            response = client.get_messages('baz', include_hidden=True)
413
359
            self.assertMessageIDs(['bar', 'foo'], response['messages'])
414
360
            response = client.get_messages('baz', include_hidden=False)
415
361
            self.assertMessageIDs(['bar'], response['messages'])
 
362
 
 
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')
 
369
 
 
370
    def test_display_type_headers_only(self):
 
371
        client = GrackleClient('localhost', 8446)
 
372
        archive = {
 
373
            'baz': [
 
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'])
 
380
        self.assertEqual(
 
381
            {'From': 'me', 'Message-Id': 'foo', 'To': 'you'},
 
382
            first_message['headers'])
 
383
        self.assertNotIn('body', first_message)
 
384
 
 
385
    def test_display_type_text_only(self):
 
386
        client = GrackleClient('localhost', 8446)
 
387
        archive = {
 
388
            'baz': [
 
389
                make_mime_message(
 
390
                    'foo', 'abcdefghi',
 
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'])
 
400
 
 
401
    def test_display_type_all(self):
 
402
        client = GrackleClient('localhost', 8447)
 
403
        archive = {
 
404
            'baz': [
 
405
                make_mime_message(
 
406
                    'foo', 'abcdefghi',
 
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'])
 
415
        self.assertEqual(
 
416
            'abcdefghi\n\nattactment data.', first_message['body'])
 
417
 
 
418
    def test_date_range(self):
 
419
        client = GrackleClient('localhost', 8448)
 
420
        archive = {
 
421
            'baz': [
 
422
                make_mime_message(
 
423
                    'foo', 'abcdefghi', headers={'date': '2011-12-31'}),
 
424
                make_mime_message(
 
425
                    'bar', 'abcdefghi', headers={'date': '2012-01-01'}),
 
426
                make_mime_message(
 
427
                    'qux', 'abcdefghi', headers={'date': '2012-01-15'}),
 
428
                make_mime_message(
 
429
                    'naf', 'abcdefghi', headers={'date': '2012-01-31'}),
 
430
                make_mime_message(
 
431
                    'doh', 'abcdefghi', headers={'date': '2012-02-01'}),
 
432
                    ]}
 
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)
 
438
 
 
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')
 
445
 
 
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..')
 
452
 
 
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')