~didrocks/unity/altf10

« back to all changes in this revision

Viewing changes to grackle/tests/test_client.py

  • Committer: Curtis Hovey
  • Date: 2012-03-17 20:57:24 UTC
  • Revision ID: curtis.hovey@canonical.com-20120317205724-2tqfvj1jqtn9fr8t
Converted gracle from a request handler to a wsgi app.
Keep Arrons brilliant ForkedFakeService.

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