~didrocks/unity/altf10

« back to all changes in this revision

Viewing changes to grackle/tests/test_client.py

  • Committer: Curtis Hovey
  • Date: 2012-03-01 01:08:15 UTC
  • Revision ID: curtis.hovey@canonical.com-20120301010815-hz0al69e3j0c9e6n
Raise ArchiveIdNotFound if the client puts a message into an unknown archive.

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