~didrocks/unity/altf10

« back to all changes in this revision

Viewing changes to grackle/tests/test_client.py

  • Committer: Aaron Bentley
  • Date: 2012-01-16 15:23:42 UTC
  • Revision ID: aaron@canonical.com-20120116152342-a501e23hpygeyjai
Switch fake_grackle_service to ForkedFake.from_client.

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