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