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