~didrocks/unity/altf10

« back to all changes in this revision

Viewing changes to grackle/tests/test_client.py

  • Committer: Curtis Hovey
  • Date: 2012-03-16 19:38:35 UTC
  • Revision ID: curtis.hovey@canonical.com-20120316193835-egu5tc1n0xlr5udj
Rename args to be honest about what is expected.

Show diffs side-by-side

added added

removed removed

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