~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 21:02:32 UTC
  • Revision ID: curtis.hovey@canonical.com-20120317210232-0cw98mbpn9356que
No need to uppercase the reason.

Show diffs side-by-side

added added

removed removed

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