~didrocks/unity/altf10

« back to all changes in this revision

Viewing changes to grackle/tests/test_client.py

  • Committer: Aaron Bentley
  • Date: 2012-01-12 11:31:51 UTC
  • Revision ID: aaron@canonical.com-20120112113151-qe327t15vwogee6t
Order by thread subject.

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