~unity-2d-team/unity-2d/Shell-MultiMonitor

« 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
  • mto: This revision was merged to the branch mainline in revision 45.
  • 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
118
 
 
119
 
 
120
 
class ForkedFake:
 
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)
 
61
 
 
62
 
 
63
class ForkedFakeService:
121
64
    """A Grackle service fake, as a ContextManager."""
122
65
 
123
66
    def __init__(self, port, messages=None, write_logs=False):
124
67
        """Constructor.
125
 
        :param port: The tcp port to use
 
68
 
 
69
        :param port: The tcp port to use.
126
70
        :param messages: A dict of lists of dicts representing messages.  The
127
71
            outer dict represents the archive, the list represents the list of
128
72
            messages for that archive.
139
83
 
140
84
    @staticmethod
141
85
    def from_client(client, messages=None):
142
 
        """Instantiate a ForkedFake from the client.
 
86
        """Instantiate a ForkedFakeService from the client.
143
87
 
144
 
        :param port: The client  to provide service for.
 
88
        :param port: The client to provide service for.
145
89
        :param messages: A dict of lists of dicts representing messages.  The
146
90
            outer dict represents the archive, the list represents the list of
147
91
            messages for that archive.
148
92
        """
149
 
        return ForkedFake(client.port, messages)
 
93
        return ForkedFakeService(client.port, messages)
150
94
 
151
95
    def is_ready(self):
152
96
        """Tell the parent process that the server is ready for writes."""
167
111
    def start_server(self):
168
112
        """Start the HTTP server."""
169
113
        service = HTTPServer(('', self.port), FakeGrackleRequestHandler)
170
 
        service.store = GrackleStore(self.messages)
 
114
        service.store = MemoryStore(self.messages)
171
115
        for archive_id, messages in service.store.messages.iteritems():
172
116
            for message in messages:
173
117
                message.setdefault('headers', {})
181
125
        os.kill(self.pid, SIGKILL)
182
126
 
183
127
 
184
 
SUPPORTED_ORDERS = set(
185
 
    ['date', 'author', 'subject', 'thread_newest', 'thread_oldest',
186
 
     'thread_subject'])
187
 
 
188
 
 
189
128
class FakeGrackleRequestHandler(BaseHTTPRequestHandler):
190
129
    """A request handler that forwards to server.store."""
191
130
 
216
155
                self.send_response(httplib.OK)
217
156
                self.end_headers()
218
157
                self.wfile.write(simplejson.dumps(response))
219
 
            except UnsupportedOrder:
220
 
                self.send_response(httplib.BAD_REQUEST)
221
 
                self.wfile.write('Unsupported order')
 
158
            except Exception, error:
 
159
                self.send_response(
 
160
                    httplib.BAD_REQUEST, error.__doc__)
222
161
                return
223
162
 
224
163
    def log_message(self, format, *args):
231
170
class TestPutMessage(TestCase):
232
171
 
233
172
    def test_put_message(self):
234
 
        client = GrackleClient('localhost', 8436)
235
 
        with ForkedFake.from_client(client):
 
173
        client = GrackleClient('localhost', 8420)
 
174
        with ForkedFakeService.from_client(client):
236
175
            client.put_message('arch1', 'asdf', StringIO('This is a message'))
237
176
            with ExpectedException(Exception, 'wtf'):
238
177
                client.put_message('arch1', 'asdf',
249
188
            sorted(ids), sorted(messages, key=lambda m: m['message_id']))
250
189
 
251
190
    def test_get_messages(self):
252
 
        client = GrackleClient('localhost', 8435)
253
 
        with ForkedFake.from_client(client,
254
 
            {'baz':
255
 
            [{'message_id': 'foo'},
256
 
             {'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):
257
195
            response = client.get_messages('baz')
258
196
        self.assertEqual(['bar', 'foo'], sorted(m['message_id'] for m in
259
197
            response['messages']))
262
200
 
263
201
    def test_get_messages_by_id(self):
264
202
        client = GrackleClient('localhost', 8437)
265
 
        with ForkedFake.from_client(client,
266
 
            {'baz':
267
 
            [{'message_id': 'foo'},
268
 
             {'message_id': 'bar'}]}):
 
203
        archive = {
 
204
            'baz': [make_message('foo'), make_message('bar')]}
 
205
        with ForkedFakeService.from_client(client, archive):
269
206
            response = client.get_messages('baz', message_ids=['foo'])
270
207
        message, = response['messages']
271
208
        self.assertEqual('foo', message['message_id'])
272
209
 
273
210
    def test_get_messages_batching(self):
274
211
        client = GrackleClient('localhost', 8438)
275
 
        with ForkedFake.from_client(client,
276
 
            {'baz':
277
 
            [{'message_id': 'foo'},
278
 
             {'message_id': 'bar'}]}):
 
212
        archive = {'baz': [make_message('foo'), make_message('bar')]}
 
213
        with ForkedFakeService.from_client(client, archive):
279
214
            response = client.get_messages('baz', limit=1)
280
215
            self.assertEqual(1, len(response['messages']))
281
216
            messages = response['messages']
287
222
 
288
223
    def get_messages_member_order_test(self, key):
289
224
        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'}]}):
 
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):
293
231
            response = client.get_messages('baz')
294
232
            self.assertIDOrder(['foo', 'bar'], response['messages'])
295
233
            response = client.get_messages('baz', order=key)
305
243
        self.get_messages_member_order_test('subject')
306
244
 
307
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
             ]}
308
253
        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
 
            ]}):
 
254
        with ForkedFakeService.from_client(client, archive):
314
255
            response = client.get_messages('baz')
315
256
            self.assertIDOrder(['bar', 'qux', 'foo'], response['messages'])
316
257
            response = client.get_messages('baz', order='subject')
320
261
 
321
262
    def test_get_messages_thread_oldest_order(self):
322
263
        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
 
            ]}):
 
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):
328
272
            response = client.get_messages('baz')
329
273
            self.assertIDOrder(['bar', 'qux', 'foo'], response['messages'])
330
274
            response = client.get_messages('baz', order='date')
334
278
 
335
279
    def test_get_messages_thread_newest_order(self):
336
280
        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
 
            ]}):
 
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):
343
291
            response = client.get_messages('baz', order='date')
344
292
            self.assertIDOrder(
345
293
                ['qux', 'bar', 'foo', 'baz'], response['messages'])
349
297
 
350
298
    def test_get_messages_unsupported_order(self):
351
299
        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'}]}):
 
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):
355
306
            with ExpectedException(UnsupportedOrder, ''):
356
307
                client.get_messages('baz', order='nonsense')
357
308
 
358
309
    def test_get_messages_headers_no_headers(self):
359
310
        client = GrackleClient('localhost', 8440)
360
 
        with ForkedFake.from_client(client,
361
 
            {'baz': [
362
 
                {'message_id': 'foo'}
363
 
            ]}):
 
311
        archive = {'baz': [make_message('foo')]}
 
312
        with ForkedFakeService.from_client(client, archive):
364
313
            response = client.get_messages('baz', headers=[
365
314
                'Subject', 'Date', 'X-Launchpad-Message-Rationale'])
366
315
        first_message = response['messages'][0]
369
318
 
370
319
    def test_get_messages_headers_exclude_headers(self):
371
320
        client = GrackleClient('localhost', 8441)
372
 
        with ForkedFake.from_client(client,
373
 
            {'baz': [
374
 
                {'message_id': 'foo', 'headers': {'From': 'me'}}
375
 
            ]}):
 
321
        archive = {
 
322
            'baz': [make_message('foo', headers={'From': 'me'})]}
 
323
        with ForkedFakeService.from_client(client, archive):
376
324
            response = client.get_messages('baz', headers=[
377
325
                'Subject', 'Date', 'X-Launchpad-Message-Rationale'])
378
326
        first_message = response['messages'][0]
381
329
 
382
330
    def test_get_messages_headers_include_headers(self):
383
331
        client = GrackleClient('localhost', 8442)
384
 
        with ForkedFake.from_client(client,
385
 
            {'baz': [
386
 
                {'message_id': 'foo', 'headers': {'From': 'me', 'To': 'you'}}
387
 
            ]}):
 
332
        archive = {
 
333
            'baz': [
 
334
                make_message('foo', headers={'From': 'me', 'To': 'you'})]}
 
335
        with ForkedFakeService.from_client(client, archive):
388
336
            response = client.get_messages('baz', headers=[
389
337
                'From', 'To'])
390
338
        first_message = response['messages'][0]
393
341
 
394
342
    def test_get_messages_max_body_length(self):
395
343
        client = GrackleClient('localhost', 8443)
396
 
        with ForkedFake.from_client(client,
397
 
            {'baz': [
398
 
                {'message_id': 'foo', 'body': u'abcdefghi'}
399
 
            ]}):
 
344
        archive = {'baz': [make_message('foo', body=u'abcdefghi')]}
 
345
        with ForkedFakeService.from_client(client, archive):
400
346
            response = client.get_messages('baz', max_body_length=3)
401
347
        first_message = response['messages'][0]
402
348
        self.assertEqual('abc', first_message['body'])
403
349
 
404
350
    def test_include_hidden(self):
405
351
        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
 
            ]}):
 
352
        archive = {
 
353
            'baz': [
 
354
                make_message('foo', hidden=True),
 
355
                make_message('bar', hidden=False),
 
356
            ]}
 
357
        with ForkedFakeService.from_client(client, archive):
411
358
            response = client.get_messages('baz', include_hidden=True)
412
359
            self.assertMessageIDs(['bar', 'foo'], response['messages'])
413
360
            response = client.get_messages('baz', include_hidden=False)
414
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')