~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 16:10:54 UTC
  • Revision ID: curtis.hovey@canonical.com-20120316161054-jk68i83kh81qm2vi
Store the body text only.

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