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

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