~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 20:41:54 UTC
  • Revision ID: curtis.hovey@canonical.com-20120316204154-nz50ae66odaqvnb4
Remove hack now that the tests use a helper that makes real messages.

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