~didrocks/unity/altf10

4 by Aaron Bentley
Initial test.
1
from BaseHTTPServer import (
2
    HTTPServer,
3
    BaseHTTPRequestHandler,
4
    )
35.1.9 by Curtis Hovey
Added support for display_type == 'text-only'.
5
from email.message import Message
6
from email.mime.multipart import MIMEMultipart
7
from email.mime.text import MIMEText
6 by Aaron Bentley
Use constants.
8
import httplib
32 by Aaron Bentley
Switch test HTTP server to standard Python logging.
9
import logging
4 by Aaron Bentley
Initial test.
10
import os
5 by Aaron Bentley
Actual fake service working.
11
from signal import SIGKILL
13 by Aaron Bentley
Retrieve messages.
12
import simplejson
4 by Aaron Bentley
Initial test.
13
from StringIO import StringIO
32 by Aaron Bentley
Switch test HTTP server to standard Python logging.
14
import sys
3 by Aaron Bentley
Add test framework.
15
from unittest import TestCase
13 by Aaron Bentley
Retrieve messages.
16
from urlparse import urlparse
15 by Aaron Bentley
Test filtering by message-id.
17
from urlparse import parse_qs
3 by Aaron Bentley
Add test framework.
18
5 by Aaron Bentley
Actual fake service working.
19
from testtools import ExpectedException
20
8 by Aaron Bentley
Test message path.
21
from grackle.client import (
22
    GrackleClient,
35.1.4 by Curtis Hovey
Added SUPPORTED_DISPLAY_TYPES.
23
    UnsupportedDisplayType,
21 by Aaron Bentley
Test unsupported orders.
24
    UnsupportedOrder,
8 by Aaron Bentley
Test message path.
25
    )
4 by Aaron Bentley
Initial test.
26
27
35.1.14 by Curtis Hovey
Create the archive outside of the call to create the server.
28
def make_message(message_id, body='body', headers=None, hidden=False):
35.1.13 by Curtis Hovey
Use make_message() to make test data consistent.
29
    if headers is None:
30
        headers = {}
31
    headers['Message-Id'] = message_id
32
    message = {
33
        'message_id': message_id,
34
        'headers': headers,
35
        'thread_id': message_id,
36
        'date': headers.get('date', '2005-01-01'),
37
        'subject': headers.get('subject', 'subject'),
35.1.14 by Curtis Hovey
Create the archive outside of the call to create the server.
38
        'author': headers.get('author', 'author'),
35.1.13 by Curtis Hovey
Use make_message() to make test data consistent.
39
        'hidden': hidden,
40
        'attachments': [],
41
        'body': body,
42
        'replies': [],
43
        }
44
    if 'in-reply-to' in headers:
45
        message['in_reply_to'] = headers['in-reply-to']
46
    return message
47
48
49
def make_mime_message(message_id, text, headers=None, hidden=False,
50
                      attachment_type=None):
35.1.9 by Curtis Hovey
Added support for display_type == 'text-only'.
51
    message = MIMEMultipart()
52
    message.attach(MIMEText(text))
53
    message['Message-Id'] = message_id
54
    if headers is not None:
55
        for k, v in headers.items():
56
            message[k] = v
57
    if attachment_type is not None:
58
        attachment = Message()
59
        attachment.set_payload('attactment data.')
60
        attachment['Content-Type'] = attachment_type
61
        attachment['Content-Disposition'] = (
62
            'attachment; filename="file.ext"')
63
        message.attach(attachment)
64
    dict_message = {
65
        'message_id': message_id,
66
        'headers': dict(message.items()),
67
        'body': message.get_payload(),
68
        }
69
    return dict_message
70
71
22 by Aaron Bentley
Order by thread subject.
72
def threaded_messages(messages):
73
    threads = {}
74
    count = 0
75
    pending = []
76
    for message in messages:
77
        if message.get('in_reply_to') is None:
78
            threads[message['message_id']] = [message]
79
            count += 1
80
        else:
81
            pending.append(message)
82
    for message in pending:
83
        threads[message['in_reply_to']].append(message)
84
    return threads.values()
85
86
28 by Aaron Bentley
Extract GrackleStore.
87
class GrackleStore:
34 by Aaron Bentley
Cleanup
88
    """A memory-backed message store."""
5 by Aaron Bentley
Actual fake service working.
89
28 by Aaron Bentley
Extract GrackleStore.
90
    def __init__(self, messages):
34 by Aaron Bentley
Cleanup
91
        """Constructor."""
12 by Aaron Bentley
Use pipe to ensure we only use HTTP once it's running.
92
        self.messages = messages
26 by Aaron Bentley
Implement an archive namespace.
93
94
    def get_messages(self, archive_id, query_string):
34 by Aaron Bentley
Cleanup
95
        """Return matching messages.
96
97
        :param archive_id: The archive to retrieve from.
98
        :param query_string: Contains 'parameters', which is a JSON-format
99
            string describing parameters.
100
        """
15 by Aaron Bentley
Test filtering by message-id.
101
        query = parse_qs(query_string)
102
        parameters = simplejson.loads(query['parameters'][0])
22 by Aaron Bentley
Order by thread subject.
103
        order = parameters.get('order')
28 by Aaron Bentley
Extract GrackleStore.
104
        messages = self.messages[archive_id]
35.1.1 by Curtis Hovey
Hush lint.
105
        if order is not None:
22 by Aaron Bentley
Order by thread subject.
106
            if order not in SUPPORTED_ORDERS:
28 by Aaron Bentley
Extract GrackleStore.
107
                raise UnsupportedOrder
23 by Aaron Bentley
Support thread_oldest order.
108
            elif order.startswith('thread_'):
22 by Aaron Bentley
Order by thread subject.
109
                threaded = threaded_messages(messages)
110
                messages = []
23 by Aaron Bentley
Support thread_oldest order.
111
                if order == 'thread_subject':
112
                    threaded.sort(key=lambda t: t[0]['subject'])
113
                if order == 'thread_oldest':
114
                    threaded.sort(key=lambda t: min(m['date'] for m in t))
24 by Aaron Bentley
Support thread_newest threading.
115
                if order == 'thread_newest':
116
                    threaded.sort(key=lambda t: max(m['date'] for m in t))
22 by Aaron Bentley
Order by thread subject.
117
                for thread in threaded:
118
                    messages.extend(thread)
119
            else:
23 by Aaron Bentley
Support thread_oldest order.
120
                messages.sort(key=lambda m: m[order])
35.1.4 by Curtis Hovey
Added SUPPORTED_DISPLAY_TYPES.
121
        display_type = parameters.get('display_type', 'all')
122
        if display_type not in SUPPORTED_DISPLAY_TYPES:
123
            raise UnsupportedDisplayType
29 by Aaron Bentley
implement include_hidden.
124
        new_messages = []
125
        for message in messages:
35.1.1 by Curtis Hovey
Hush lint.
126
            if (not parameters['include_hidden']
29 by Aaron Bentley
implement include_hidden.
127
                and message.get('hidden', False)):
128
                continue
129
35.1.1 by Curtis Hovey
Hush lint.
130
            if ('message_ids' in parameters
131
                and message['message_id'] not in parameters['message_ids']):
29 by Aaron Bentley
implement include_hidden.
132
                continue
133
            message = dict(message)
134
            if 'headers' in parameters:
135
                headers = dict(
136
                    (k, v) for k, v in message['headers'].iteritems()
137
                    if k in parameters['headers'])
138
                message['headers'] = headers
139
            max_body = parameters.get('max_body_length')
35.1.6 by Curtis Hovey
Added display_type == 'headers-only' support.
140
            if display_type == 'headers-only':
141
                del message['body']
35.1.9 by Curtis Hovey
Added support for display_type == 'text-only'.
142
            elif (display_type == 'text-only'
143
                  and isinstance(message['body'], list)):
144
                text_parts = [
145
                    part.get_payload() for part in message['body']
146
                    if part.get_content_type() == 'text/plain']
35.1.10 by Curtis Hovey
Added support for display_type == 'all'.
147
                message['body'] = '\n\n'.join(text_parts)
148
            elif (display_type == 'all'
149
                  and 'body' in message
150
                  and isinstance(message['body'], list)):
151
                parts = [str(part.get_payload()) for part in message['body']]
152
                message['body'] = '\n\n'.join(parts)
153
            if max_body is not None and display_type != 'headers-only':
29 by Aaron Bentley
implement include_hidden.
154
                message['body'] = message['body'][:max_body]
155
            new_messages.append(message)
156
        messages = new_messages
19 by Aaron Bentley
Implement memo/limit support.
157
        limit = parameters.get('limit', 100)
158
        memo = parameters.get('memo')
159
        message_id_indices = dict(
160
            (m['message_id'], idx) for idx, m in enumerate(messages))
161
        if memo is None:
162
            start = 0
163
        else:
164
            start = message_id_indices[memo.encode('rot13')]
165
        if start > 0:
166
            previous_memo = messages[start - 1]['message_id'].encode('rot13')
167
        else:
168
            previous_memo = None
169
        end = min(start + limit, len(messages))
170
        if end < len(messages):
171
            next_memo = messages[end]['message_id'].encode('rot13')
172
        else:
173
            next_memo = None
174
        messages = messages[start:end]
29 by Aaron Bentley
implement include_hidden.
175
16 by Aaron Bentley
Include next_memo, previous_memo in get_messages response.
176
        response = {
29 by Aaron Bentley
implement include_hidden.
177
            'messages': messages,
19 by Aaron Bentley
Implement memo/limit support.
178
            'next_memo': next_memo,
179
            'previous_memo': previous_memo
16 by Aaron Bentley
Include next_memo, previous_memo in get_messages response.
180
            }
28 by Aaron Bentley
Extract GrackleStore.
181
        return response
182
183
35.1.3 by Curtis Hovey
Renamed ForkedFake => ForkedFakeService.
184
class ForkedFakeService:
34 by Aaron Bentley
Cleanup
185
    """A Grackle service fake, as a ContextManager."""
28 by Aaron Bentley
Extract GrackleStore.
186
33 by Aaron Bentley
Cleaner logging switch.
187
    def __init__(self, port, messages=None, write_logs=False):
34 by Aaron Bentley
Cleanup
188
        """Constructor.
35.1.3 by Curtis Hovey
Renamed ForkedFake => ForkedFakeService.
189
190
        :param port: The tcp port to use.
34 by Aaron Bentley
Cleanup
191
        :param messages: A dict of lists of dicts representing messages.  The
192
            outer dict represents the archive, the list represents the list of
193
            messages for that archive.
194
        :param write_logs: If true, log messages will be written to stdout.
195
        """
28 by Aaron Bentley
Extract GrackleStore.
196
        self.pid = None
197
        self.port = port
31 by Aaron Bentley
Switch fake_grackle_service to ForkedFake.from_client.
198
        if messages is None:
199
            self.messages = {}
200
        else:
201
            self.messages = messages
28 by Aaron Bentley
Extract GrackleStore.
202
        self.read_end, self.write_end = os.pipe()
33 by Aaron Bentley
Cleaner logging switch.
203
        self.write_logs = write_logs
28 by Aaron Bentley
Extract GrackleStore.
204
31 by Aaron Bentley
Switch fake_grackle_service to ForkedFake.from_client.
205
    @staticmethod
206
    def from_client(client, messages=None):
35.1.3 by Curtis Hovey
Renamed ForkedFake => ForkedFakeService.
207
        """Instantiate a ForkedFakeService from the client.
34 by Aaron Bentley
Cleanup
208
35.1.3 by Curtis Hovey
Renamed ForkedFake => ForkedFakeService.
209
        :param port: The client to provide service for.
34 by Aaron Bentley
Cleanup
210
        :param messages: A dict of lists of dicts representing messages.  The
211
            outer dict represents the archive, the list represents the list of
212
            messages for that archive.
213
        """
35.1.3 by Curtis Hovey
Renamed ForkedFake => ForkedFakeService.
214
        return ForkedFakeService(client.port, messages)
31 by Aaron Bentley
Switch fake_grackle_service to ForkedFake.from_client.
215
28 by Aaron Bentley
Extract GrackleStore.
216
    def is_ready(self):
34 by Aaron Bentley
Cleanup
217
        """Tell the parent process that the server is ready for writes."""
28 by Aaron Bentley
Extract GrackleStore.
218
        os.write(self.write_end, 'asdf')
219
220
    def __enter__(self):
34 by Aaron Bentley
Cleanup
221
        """Run the service.
222
223
        Fork and start a server in the child.  Return when the server is ready
224
        for use."""
28 by Aaron Bentley
Extract GrackleStore.
225
        pid = os.fork()
226
        if pid == 0:
227
            self.start_server()
228
        self.pid = pid
229
        os.read(self.read_end, 1)
230
        return
231
232
    def start_server(self):
34 by Aaron Bentley
Cleanup
233
        """Start the HTTP server."""
28 by Aaron Bentley
Extract GrackleStore.
234
        service = HTTPServer(('', self.port), FakeGrackleRequestHandler)
235
        service.store = GrackleStore(self.messages)
236
        for archive_id, messages in service.store.messages.iteritems():
237
            for message in messages:
238
                message.setdefault('headers', {})
239
        self.is_ready()
33 by Aaron Bentley
Cleaner logging switch.
240
        if self.write_logs:
241
            logging.basicConfig(
242
                stream=sys.stderr, level=logging.INFO)
28 by Aaron Bentley
Extract GrackleStore.
243
        service.serve_forever()
244
245
    def __exit__(self, exc_type, exc_val, traceback):
246
        os.kill(self.pid, SIGKILL)
247
248
35.1.4 by Curtis Hovey
Added SUPPORTED_DISPLAY_TYPES.
249
SUPPORTED_DISPLAY_TYPES = set(['all', 'text-only', 'headers-only'])
250
251
28 by Aaron Bentley
Extract GrackleStore.
252
SUPPORTED_ORDERS = set(
253
    ['date', 'author', 'subject', 'thread_newest', 'thread_oldest',
254
     'thread_subject'])
255
256
257
class FakeGrackleRequestHandler(BaseHTTPRequestHandler):
34 by Aaron Bentley
Cleanup
258
    """A request handler that forwards to server.store."""
28 by Aaron Bentley
Extract GrackleStore.
259
32 by Aaron Bentley
Switch test HTTP server to standard Python logging.
260
    def __init__(self, *args, **kwargs):
34 by Aaron Bentley
Cleanup
261
        """Constructor.  Sets up logging."""
32 by Aaron Bentley
Switch test HTTP server to standard Python logging.
262
        self.logger = logging.getLogger('http')
263
        BaseHTTPRequestHandler.__init__(self, *args, **kwargs)
264
28 by Aaron Bentley
Extract GrackleStore.
265
    def do_POST(self):
34 by Aaron Bentley
Cleanup
266
        """Create a message on POST."""
28 by Aaron Bentley
Extract GrackleStore.
267
        message = self.rfile.read(int(self.headers['content-length']))
268
        if message == 'This is a message':
269
            self.send_response(httplib.CREATED)
270
            self.end_headers()
271
            self.wfile.close()
272
        else:
273
            self.send_error(httplib.BAD_REQUEST)
274
275
    def do_GET(self):
34 by Aaron Bentley
Cleanup
276
        """Retrieve a list of messages on GET."""
28 by Aaron Bentley
Extract GrackleStore.
277
        scheme, netloc, path, params, query_string, fragments = (
278
            urlparse(self.path))
279
        parts = path.split('/')
280
        if parts[1] == 'archive':
281
            try:
282
                response = self.server.store.get_messages(
283
                    parts[2], query_string)
284
                self.send_response(httplib.OK)
285
                self.end_headers()
286
                self.wfile.write(simplejson.dumps(response))
35.1.8 by Curtis Hovey
Use the exception __doc__ to ensure client and server can match exceptions.
287
            except UnsupportedOrder:
288
                self.send_response(
289
                    httplib.BAD_REQUEST, UnsupportedOrder.__doc__)
290
                return
35.1.7 by Curtis Hovey
Moved the handling of unsupported display_type to server.
291
            except UnsupportedDisplayType:
292
                self.send_response(
35.1.8 by Curtis Hovey
Use the exception __doc__ to ensure client and server can match exceptions.
293
                    httplib.BAD_REQUEST, UnsupportedDisplayType.__doc__)
28 by Aaron Bentley
Extract GrackleStore.
294
                return
13 by Aaron Bentley
Retrieve messages.
295
32 by Aaron Bentley
Switch test HTTP server to standard Python logging.
296
    def log_message(self, format, *args):
34 by Aaron Bentley
Cleanup
297
        """Override log_message to use standard Python logging."""
32 by Aaron Bentley
Switch test HTTP server to standard Python logging.
298
        message = "%s - - [%s] %s\n" % (
35.1.1 by Curtis Hovey
Hush lint.
299
            self.address_string(), self.log_date_time_string(), format % args)
32 by Aaron Bentley
Switch test HTTP server to standard Python logging.
300
        self.logger.info(message)
301
5 by Aaron Bentley
Actual fake service working.
302
3 by Aaron Bentley
Add test framework.
303
class TestPutMessage(TestCase):
304
305
    def test_put_message(self):
12 by Aaron Bentley
Use pipe to ensure we only use HTTP once it's running.
306
        client = GrackleClient('localhost', 8436)
35.1.3 by Curtis Hovey
Renamed ForkedFake => ForkedFakeService.
307
        with ForkedFakeService.from_client(client):
7 by Aaron Bentley
Fix URLs etc.
308
            client.put_message('arch1', 'asdf', StringIO('This is a message'))
5 by Aaron Bentley
Actual fake service working.
309
            with ExpectedException(Exception, 'wtf'):
7 by Aaron Bentley
Fix URLs etc.
310
                client.put_message('arch1', 'asdf',
311
                    StringIO('This is not a message'))
11 by Aaron Bentley
Start working on GET.
312
313
314
class TestGetMessages(TestCase):
315
20 by Aaron Bentley
Support order by date
316
    def assertIDOrder(self, ids, messages):
317
        self.assertEqual(ids, [m['message_id'] for m in messages])
318
19 by Aaron Bentley
Implement memo/limit support.
319
    def assertMessageIDs(self, ids, messages):
20 by Aaron Bentley
Support order by date
320
        self.assertIDOrder(
35.1.1 by Curtis Hovey
Hush lint.
321
            sorted(ids), sorted(messages, key=lambda m: m['message_id']))
19 by Aaron Bentley
Implement memo/limit support.
322
11 by Aaron Bentley
Start working on GET.
323
    def test_get_messages(self):
324
        client = GrackleClient('localhost', 8435)
35.1.14 by Curtis Hovey
Create the archive outside of the call to create the server.
325
        archive = {
326
            'baz': [make_message('foo'), make_message('bar')]}
327
        with ForkedFakeService.from_client(client, archive):
15 by Aaron Bentley
Test filtering by message-id.
328
            response = client.get_messages('baz')
17 by Aaron Bentley
Switch hyphens to underscores.
329
        self.assertEqual(['bar', 'foo'], sorted(m['message_id'] for m in
16 by Aaron Bentley
Include next_memo, previous_memo in get_messages response.
330
            response['messages']))
331
        self.assertIs(None, response['next_memo'])
332
        self.assertIs(None, response['previous_memo'])
15 by Aaron Bentley
Test filtering by message-id.
333
334
    def test_get_messages_by_id(self):
335
        client = GrackleClient('localhost', 8437)
35.1.14 by Curtis Hovey
Create the archive outside of the call to create the server.
336
        archive = {
337
            'baz': [make_message('foo'), make_message('bar')]}
338
        with ForkedFakeService.from_client(client, archive):
16 by Aaron Bentley
Include next_memo, previous_memo in get_messages response.
339
            response = client.get_messages('baz', message_ids=['foo'])
340
        message, = response['messages']
17 by Aaron Bentley
Switch hyphens to underscores.
341
        self.assertEqual('foo', message['message_id'])
19 by Aaron Bentley
Implement memo/limit support.
342
343
    def test_get_messages_batching(self):
20 by Aaron Bentley
Support order by date
344
        client = GrackleClient('localhost', 8438)
35.1.14 by Curtis Hovey
Create the archive outside of the call to create the server.
345
        archive = {'baz': [make_message('foo'), make_message('bar')]}
346
        with ForkedFakeService.from_client(client, archive):
19 by Aaron Bentley
Implement memo/limit support.
347
            response = client.get_messages('baz', limit=1)
348
            self.assertEqual(1, len(response['messages']))
349
            messages = response['messages']
350
            response = client.get_messages(
351
                'baz', limit=1, memo=response['next_memo'])
352
            self.assertEqual(1, len(response['messages']))
353
            messages.extend(response['messages'])
354
            self.assertMessageIDs(['foo', 'bar'], messages)
20 by Aaron Bentley
Support order by date
355
22 by Aaron Bentley
Order by thread subject.
356
    def get_messages_member_order_test(self, key):
20 by Aaron Bentley
Support order by date
357
        client = GrackleClient('localhost', 8439)
35.1.14 by Curtis Hovey
Create the archive outside of the call to create the server.
358
        archive = {
359
            'baz': [
360
                make_message('foo', headers={key: '2011-03-25'}),
361
                make_message('bar', headers={key: '2011-03-24'}),
362
             ]}
363
        with ForkedFakeService.from_client(client, archive):
20 by Aaron Bentley
Support order by date
364
            response = client.get_messages('baz')
365
            self.assertIDOrder(['foo', 'bar'], response['messages'])
22 by Aaron Bentley
Order by thread subject.
366
            response = client.get_messages('baz', order=key)
20 by Aaron Bentley
Support order by date
367
            self.assertIDOrder(['bar', 'foo'], response['messages'])
21 by Aaron Bentley
Test unsupported orders.
368
22 by Aaron Bentley
Order by thread subject.
369
    def test_get_messages_date_order(self):
370
        self.get_messages_member_order_test('date')
371
372
    def test_get_messages_author_order(self):
373
        self.get_messages_member_order_test('author')
374
375
    def test_get_messages_subject_order(self):
376
        self.get_messages_member_order_test('subject')
377
378
    def test_get_messages_thread_subject_order(self):
35.1.14 by Curtis Hovey
Create the archive outside of the call to create the server.
379
        archive = {
380
            'baz': [
381
                make_message('bar', headers={'subject': 'y'}),
382
                make_message('qux', headers={'subject': 'z'}),
383
                make_message('foo', headers={'subject': 'x',
384
                                             'in-reply-to': 'qux'}),
385
             ]}
22 by Aaron Bentley
Order by thread subject.
386
        client = GrackleClient('localhost', 8439)
35.1.13 by Curtis Hovey
Use make_message() to make test data consistent.
387
        with ForkedFakeService.from_client(client, archive):
22 by Aaron Bentley
Order by thread subject.
388
            response = client.get_messages('baz')
389
            self.assertIDOrder(['bar', 'qux', 'foo'], response['messages'])
390
            response = client.get_messages('baz', order='subject')
391
            self.assertIDOrder(['foo', 'bar', 'qux'], response['messages'])
392
            response = client.get_messages('baz', order='thread_subject')
393
            self.assertIDOrder(['bar', 'qux', 'foo'], response['messages'])
394
23 by Aaron Bentley
Support thread_oldest order.
395
    def test_get_messages_thread_oldest_order(self):
396
        client = GrackleClient('localhost', 8439)
35.1.14 by Curtis Hovey
Create the archive outside of the call to create the server.
397
        archive = {
398
            'baz': [
399
                make_message('bar', headers={'date': 'x'}),
400
                make_message('qux', headers={'date': 'z'}),
401
                make_message('foo', headers={'date': 'y',
402
                                             'in-reply-to': 'qux'}),
35.1.13 by Curtis Hovey
Use make_message() to make test data consistent.
403
            ]}
404
        with ForkedFakeService.from_client(client, archive):
23 by Aaron Bentley
Support thread_oldest order.
405
            response = client.get_messages('baz')
406
            self.assertIDOrder(['bar', 'qux', 'foo'], response['messages'])
407
            response = client.get_messages('baz', order='date')
408
            self.assertIDOrder(['bar', 'foo', 'qux'], response['messages'])
409
            response = client.get_messages('baz', order='thread_oldest')
410
            self.assertIDOrder(['bar', 'qux', 'foo'], response['messages'])
411
24 by Aaron Bentley
Support thread_newest threading.
412
    def test_get_messages_thread_newest_order(self):
413
        client = GrackleClient('localhost', 8439)
35.1.14 by Curtis Hovey
Create the archive outside of the call to create the server.
414
        archive = {
415
            'baz': [
416
                make_message('bar', headers={'date': 'x'}),
417
                make_message('qux', headers={'date': 'w'}),
418
                make_message('foo', headers={'date': 'y',
419
                                             'in-reply-to': 'bar'}),
420
                make_message('baz', headers={'date': 'z',
421
                                             'in-reply-to': 'qux'}),
422
            ]}
423
        with ForkedFakeService.from_client(client, archive):
24 by Aaron Bentley
Support thread_newest threading.
424
            response = client.get_messages('baz', order='date')
425
            self.assertIDOrder(
426
                ['qux', 'bar', 'foo', 'baz'], response['messages'])
427
            response = client.get_messages('baz', order='thread_newest')
428
            self.assertIDOrder(
429
                ['bar', 'foo', 'qux', 'baz'], response['messages'])
430
21 by Aaron Bentley
Test unsupported orders.
431
    def test_get_messages_unsupported_order(self):
432
        client = GrackleClient('localhost', 8439)
35.1.14 by Curtis Hovey
Create the archive outside of the call to create the server.
433
        archive = {
434
            'baz': [
35.1.13 by Curtis Hovey
Use make_message() to make test data consistent.
435
                make_message('foo', headers={'date': '2011-03-25'}),
35.1.14 by Curtis Hovey
Create the archive outside of the call to create the server.
436
                make_message('foo', headers={'date': '2011-03-24'}),
437
            ]}
438
        with ForkedFakeService.from_client(client, archive):
35 by William Grant
Fix test.
439
            with ExpectedException(UnsupportedOrder, ''):
21 by Aaron Bentley
Test unsupported orders.
440
                client.get_messages('baz', order='nonsense')
27 by Aaron Bentley
get_messages supports header parameter.
441
442
    def test_get_messages_headers_no_headers(self):
443
        client = GrackleClient('localhost', 8440)
35.1.14 by Curtis Hovey
Create the archive outside of the call to create the server.
444
        archive = {'baz': [make_message('foo')]}
445
        with ForkedFakeService.from_client(client, archive):
27 by Aaron Bentley
get_messages supports header parameter.
446
            response = client.get_messages('baz', headers=[
447
                'Subject', 'Date', 'X-Launchpad-Message-Rationale'])
448
        first_message = response['messages'][0]
449
        self.assertEqual('foo', first_message['message_id'])
450
        self.assertEqual({}, first_message['headers'])
451
452
    def test_get_messages_headers_exclude_headers(self):
29 by Aaron Bentley
implement include_hidden.
453
        client = GrackleClient('localhost', 8441)
35.1.14 by Curtis Hovey
Create the archive outside of the call to create the server.
454
        archive = {
455
            'baz': [make_message('foo', headers={'From': 'me'})]}
456
        with ForkedFakeService.from_client(client, archive):
27 by Aaron Bentley
get_messages supports header parameter.
457
            response = client.get_messages('baz', headers=[
458
                'Subject', 'Date', 'X-Launchpad-Message-Rationale'])
459
        first_message = response['messages'][0]
460
        self.assertEqual('foo', first_message['message_id'])
461
        self.assertEqual({}, first_message['headers'])
462
463
    def test_get_messages_headers_include_headers(self):
29 by Aaron Bentley
implement include_hidden.
464
        client = GrackleClient('localhost', 8442)
35.1.14 by Curtis Hovey
Create the archive outside of the call to create the server.
465
        archive = {
466
            'baz': [
467
                make_message('foo', headers={'From': 'me', 'To': 'you'})]}
468
        with ForkedFakeService.from_client(client, archive):
27 by Aaron Bentley
get_messages supports header parameter.
469
            response = client.get_messages('baz', headers=[
470
                'From', 'To'])
471
        first_message = response['messages'][0]
472
        self.assertEqual('foo', first_message['message_id'])
473
        self.assertEqual({'From': 'me', 'To': 'you'}, first_message['headers'])
28 by Aaron Bentley
Extract GrackleStore.
474
475
    def test_get_messages_max_body_length(self):
29 by Aaron Bentley
implement include_hidden.
476
        client = GrackleClient('localhost', 8443)
35.1.14 by Curtis Hovey
Create the archive outside of the call to create the server.
477
        archive = {'baz': [make_message('foo', body=u'abcdefghi')]}
478
        with ForkedFakeService.from_client(client, archive):
28 by Aaron Bentley
Extract GrackleStore.
479
            response = client.get_messages('baz', max_body_length=3)
480
        first_message = response['messages'][0]
481
        self.assertEqual('abc', first_message['body'])
482
29 by Aaron Bentley
implement include_hidden.
483
    def test_include_hidden(self):
484
        client = GrackleClient('localhost', 8444)
35.1.14 by Curtis Hovey
Create the archive outside of the call to create the server.
485
        archive = {
486
            'baz': [
35.1.13 by Curtis Hovey
Use make_message() to make test data consistent.
487
                make_message('foo', hidden=True),
35.1.14 by Curtis Hovey
Create the archive outside of the call to create the server.
488
                make_message('bar', hidden=False),
489
            ]}
490
        with ForkedFakeService.from_client(client, archive):
29 by Aaron Bentley
implement include_hidden.
491
            response = client.get_messages('baz', include_hidden=True)
492
            self.assertMessageIDs(['bar', 'foo'], response['messages'])
493
            response = client.get_messages('baz', include_hidden=False)
494
            self.assertMessageIDs(['bar'], response['messages'])
35.1.4 by Curtis Hovey
Added SUPPORTED_DISPLAY_TYPES.
495
496
    def test_display_type_unknown_value(self):
35.1.5 by Curtis Hovey
Moved the display_type arg.
497
        client = GrackleClient('localhost', 8445)
35.1.14 by Curtis Hovey
Create the archive outside of the call to create the server.
498
        archive = {'baz': [make_message('foo', body=u'abcdefghi')]}
499
        with ForkedFakeService.from_client(client, archive):
35.1.4 by Curtis Hovey
Added SUPPORTED_DISPLAY_TYPES.
500
            with ExpectedException(UnsupportedDisplayType, ''):
501
                client.get_messages('baz', display_type='unknown')
35.1.6 by Curtis Hovey
Added display_type == 'headers-only' support.
502
503
    def test_display_type_headers_only(self):
35.1.9 by Curtis Hovey
Added support for display_type == 'text-only'.
504
        client = GrackleClient('localhost', 8446)
35.1.14 by Curtis Hovey
Create the archive outside of the call to create the server.
505
        archive = {
506
            'baz': [
35.1.13 by Curtis Hovey
Use make_message() to make test data consistent.
507
                make_message('foo', body=u'abcdefghi',
35.1.14 by Curtis Hovey
Create the archive outside of the call to create the server.
508
                             headers={'From': 'me', 'To': 'you'})]}
509
        with ForkedFakeService.from_client(client, archive):
35.1.6 by Curtis Hovey
Added display_type == 'headers-only' support.
510
            response = client.get_messages('baz', display_type='headers-only')
511
        first_message = response['messages'][0]
512
        self.assertEqual('foo', first_message['message_id'])
35.1.13 by Curtis Hovey
Use make_message() to make test data consistent.
513
        self.assertEqual(
514
            {'From': 'me', 'Message-Id': 'foo', 'To': 'you'},
515
            first_message['headers'])
35.1.6 by Curtis Hovey
Added display_type == 'headers-only' support.
516
        self.assertNotIn('body', first_message)
35.1.9 by Curtis Hovey
Added support for display_type == 'text-only'.
517
518
    def test_display_type_text_only(self):
519
        client = GrackleClient('localhost', 8446)
35.1.14 by Curtis Hovey
Create the archive outside of the call to create the server.
520
        archive = {
521
            'baz': [
35.1.12 by Curtis Hovey
Renamed make_message => make_mime_message.
522
                make_mime_message(
35.1.11 by Curtis Hovey
Reanme make_json_message => make_message.
523
                    'foo', 'abcdefghi',
35.1.9 by Curtis Hovey
Added support for display_type == 'text-only'.
524
                    headers={'From': 'me', 'To': 'you'},
35.1.14 by Curtis Hovey
Create the archive outside of the call to create the server.
525
                    attachment_type='text/x-diff')]}
526
        with ForkedFakeService.from_client(client, archive):
35.1.9 by Curtis Hovey
Added support for display_type == 'text-only'.
527
            response = client.get_messages('baz', display_type='text-only')
528
        first_message = response['messages'][0]
529
        self.assertEqual('foo', first_message['message_id'])
530
        self.assertEqual('me', first_message['headers']['From'])
531
        self.assertEqual('you', first_message['headers']['To'])
35.1.10 by Curtis Hovey
Added support for display_type == 'all'.
532
        self.assertEqual('abcdefghi', first_message['body'])
533
534
    def test_display_type_all(self):
535
        client = GrackleClient('localhost', 8447)
35.1.14 by Curtis Hovey
Create the archive outside of the call to create the server.
536
        archive = {
537
            'baz': [
35.1.12 by Curtis Hovey
Renamed make_message => make_mime_message.
538
                make_mime_message(
35.1.11 by Curtis Hovey
Reanme make_json_message => make_message.
539
                    'foo', 'abcdefghi',
35.1.10 by Curtis Hovey
Added support for display_type == 'all'.
540
                    headers={'From': 'me', 'To': 'you'},
35.1.14 by Curtis Hovey
Create the archive outside of the call to create the server.
541
                    attachment_type='text/x-diff')]}
542
        with ForkedFakeService.from_client(client, archive):
35.1.10 by Curtis Hovey
Added support for display_type == 'all'.
543
            response = client.get_messages('baz', display_type='all')
544
        first_message = response['messages'][0]
545
        self.assertEqual('foo', first_message['message_id'])
546
        self.assertEqual('me', first_message['headers']['From'])
547
        self.assertEqual('you', first_message['headers']['To'])
548
        self.assertEqual(
549
            'abcdefghi\n\nattactment data.', first_message['body'])