~didrocks/unity/altf10

« back to all changes in this revision

Viewing changes to grackle/tests/test_client.py

  • Committer: Curtis Hovey
  • Date: 2012-01-30 18:31:19 UTC
  • mfrom: (35.1.2 client-get-messages-0)
  • Revision ID: curtis.hovey@canonical.com-20120130183119-ylmo65mur5ogktud
Merged lint fixes.

Show diffs side-by-side

added added

removed removed

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