~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 19:49:31 UTC
  • Revision ID: curtis.hovey@canonical.com-20120316194931-xngt1fdw9ewovwqn
Use PUT for creating 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
6
9
import logging
7
10
import os
11
14
import sys
12
15
from unittest import TestCase
13
16
from urlparse import urlparse
14
 
from urlparse import parse_qs
15
17
 
16
18
from testtools import ExpectedException
17
19
 
18
20
from grackle.client import (
19
21
    GrackleClient,
 
22
    UnparsableDateRange,
 
23
    UnsupportedDisplayType,
20
24
    UnsupportedOrder,
21
25
    )
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:
 
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:
121
64
    """A Grackle service fake, as a ContextManager."""
122
65
 
123
 
    def __init__(self, port, messages=None, write_logs=False):
 
66
    def __init__(self, port, message_archives=None, write_logs=False):
124
67
        """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.
 
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.
129
73
        :param write_logs: If true, log messages will be written to stdout.
130
74
        """
131
75
        self.pid = None
132
76
        self.port = port
133
 
        if messages is None:
134
 
            self.messages = {}
 
77
        if message_archives is None:
 
78
            self.message_archives = {}
135
79
        else:
136
 
            self.messages = messages
 
80
            self.message_archives = message_archives
137
81
        self.read_end, self.write_end = os.pipe()
138
82
        self.write_logs = write_logs
139
83
 
140
84
    @staticmethod
141
 
    def from_client(client, messages=None):
142
 
        """Instantiate a ForkedFake from the client.
 
85
    def from_client(client, message_archives=None):
 
86
        """Instantiate a ForkedFakeService from the client.
143
87
 
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.
 
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.
148
92
        """
149
 
        return ForkedFake(client.port, messages)
 
93
        return ForkedFakeService(client.port, message_archives)
150
94
 
151
95
    def is_ready(self):
152
96
        """Tell the parent process that the server is ready for writes."""
167
111
    def start_server(self):
168
112
        """Start the HTTP server."""
169
113
        service = HTTPServer(('', self.port), FakeGrackleRequestHandler)
170
 
        service.store = GrackleStore(self.messages)
171
 
        for archive_id, messages in service.store.messages.iteritems():
 
114
        service.store = MemoryStore(self.message_archives)
 
115
        for archive_id, messages in service.store.message_archives.iteritems():
172
116
            for message in messages:
173
117
                message.setdefault('headers', {})
174
118
        self.is_ready()
181
125
        os.kill(self.pid, SIGKILL)
182
126
 
183
127
 
184
 
SUPPORTED_ORDERS = set(
185
 
    ['date', 'author', 'subject', 'thread_newest', 'thread_oldest',
186
 
     'thread_subject'])
187
 
 
188
 
 
189
128
class FakeGrackleRequestHandler(BaseHTTPRequestHandler):
190
129
    """A request handler that forwards to server.store."""
191
130
 
194
133
        self.logger = logging.getLogger('http')
195
134
        BaseHTTPRequestHandler.__init__(self, *args, **kwargs)
196
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
 
197
155
    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)
 
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)
206
174
 
207
175
    def do_GET(self):
208
176
        """Retrieve a list of messages on GET."""
216
184
                self.send_response(httplib.OK)
217
185
                self.end_headers()
218
186
                self.wfile.write(simplejson.dumps(response))
219
 
            except UnsupportedOrder:
220
 
                self.send_response(httplib.BAD_REQUEST)
221
 
                self.wfile.write('Unsupported order')
 
187
            except Exception, error:
 
188
                self.send_response(
 
189
                    httplib.BAD_REQUEST, error.__doc__)
222
190
                return
223
191
 
224
192
    def log_message(self, format, *args):
231
199
class TestPutMessage(TestCase):
232
200
 
233
201
    def test_put_message(self):
234
 
        client = GrackleClient('localhost', 8436)
235
 
        with ForkedFake.from_client(client):
236
 
            client.put_message('arch1', 'asdf', StringIO('This is a message'))
 
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):
237
215
            with ExpectedException(Exception, 'wtf'):
238
 
                client.put_message('arch1', 'asdf',
239
 
                    StringIO('This is not a message'))
 
216
                client.put_message('no-archive', 'id1', StringIO('message'))
240
217
 
241
218
 
242
219
class TestGetMessages(TestCase):
249
226
            sorted(ids), sorted(messages, key=lambda m: m['message_id']))
250
227
 
251
228
    def test_get_messages(self):
252
 
        client = GrackleClient('localhost', 8435)
253
 
        with ForkedFake.from_client(client,
254
 
            {'baz':
255
 
            [{'message_id': 'foo'},
256
 
             {'message_id': 'bar'}]}):
 
229
        client = GrackleClient('localhost', 8430)
 
230
        archive = {
 
231
            'baz': [make_message('foo'), make_message('bar')]}
 
232
        with ForkedFakeService.from_client(client, archive):
257
233
            response = client.get_messages('baz')
258
234
        self.assertEqual(['bar', 'foo'], sorted(m['message_id'] for m in
259
235
            response['messages']))
262
238
 
263
239
    def test_get_messages_by_id(self):
264
240
        client = GrackleClient('localhost', 8437)
265
 
        with ForkedFake.from_client(client,
266
 
            {'baz':
267
 
            [{'message_id': 'foo'},
268
 
             {'message_id': 'bar'}]}):
 
241
        archive = {
 
242
            'baz': [make_message('foo'), make_message('bar')]}
 
243
        with ForkedFakeService.from_client(client, archive):
269
244
            response = client.get_messages('baz', message_ids=['foo'])
270
245
        message, = response['messages']
271
246
        self.assertEqual('foo', message['message_id'])
272
247
 
273
248
    def test_get_messages_batching(self):
274
249
        client = GrackleClient('localhost', 8438)
275
 
        with ForkedFake.from_client(client,
276
 
            {'baz':
277
 
            [{'message_id': 'foo'},
278
 
             {'message_id': 'bar'}]}):
 
250
        archive = {'baz': [make_message('foo'), make_message('bar')]}
 
251
        with ForkedFakeService.from_client(client, archive):
279
252
            response = client.get_messages('baz', limit=1)
280
253
            self.assertEqual(1, len(response['messages']))
281
254
            messages = response['messages']
287
260
 
288
261
    def get_messages_member_order_test(self, key):
289
262
        client = GrackleClient('localhost', 8439)
290
 
        with ForkedFake.from_client(client,
291
 
                {'baz': [{'message_id': 'foo', key: '2011-03-25'},
292
 
                 {'message_id': 'bar', key: '2011-03-24'}]}):
 
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):
293
273
            response = client.get_messages('baz')
294
274
            self.assertIDOrder(['foo', 'bar'], response['messages'])
295
275
            response = client.get_messages('baz', order=key)
305
285
        self.get_messages_member_order_test('subject')
306
286
 
307
287
    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
             ]}
308
295
        client = GrackleClient('localhost', 8439)
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
 
            ]}):
 
296
        with ForkedFakeService.from_client(client, archive):
314
297
            response = client.get_messages('baz')
315
298
            self.assertIDOrder(['bar', 'qux', 'foo'], response['messages'])
316
299
            response = client.get_messages('baz', order='subject')
320
303
 
321
304
    def test_get_messages_thread_oldest_order(self):
322
305
        client = GrackleClient('localhost', 8439)
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
 
            ]}):
 
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):
328
314
            response = client.get_messages('baz')
329
315
            self.assertIDOrder(['bar', 'qux', 'foo'], response['messages'])
330
316
            response = client.get_messages('baz', order='date')
334
320
 
335
321
    def test_get_messages_thread_newest_order(self):
336
322
        client = GrackleClient('localhost', 8439)
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
 
            ]}):
 
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):
343
333
            response = client.get_messages('baz', order='date')
344
334
            self.assertIDOrder(
345
335
                ['qux', 'bar', 'foo', 'baz'], response['messages'])
349
339
 
350
340
    def test_get_messages_unsupported_order(self):
351
341
        client = GrackleClient('localhost', 8439)
352
 
        with ForkedFake.from_client(client,
353
 
                {'baz': [{'message_id': 'foo', 'date': '2011-03-25'},
354
 
                 {'message_id': 'bar', 'date': '2011-03-24'}]}):
 
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):
355
348
            with ExpectedException(UnsupportedOrder, ''):
356
349
                client.get_messages('baz', order='nonsense')
357
350
 
358
351
    def test_get_messages_headers_no_headers(self):
359
352
        client = GrackleClient('localhost', 8440)
360
 
        with ForkedFake.from_client(client,
361
 
            {'baz': [
362
 
                {'message_id': 'foo'}
363
 
            ]}):
 
353
        archive = {'baz': [make_message('foo')]}
 
354
        with ForkedFakeService.from_client(client, archive):
364
355
            response = client.get_messages('baz', headers=[
365
356
                'Subject', 'Date', 'X-Launchpad-Message-Rationale'])
366
357
        first_message = response['messages'][0]
369
360
 
370
361
    def test_get_messages_headers_exclude_headers(self):
371
362
        client = GrackleClient('localhost', 8441)
372
 
        with ForkedFake.from_client(client,
373
 
            {'baz': [
374
 
                {'message_id': 'foo', 'headers': {'From': 'me'}}
375
 
            ]}):
 
363
        archive = {
 
364
            'baz': [make_message('foo', headers={'From': 'me'})]}
 
365
        with ForkedFakeService.from_client(client, archive):
376
366
            response = client.get_messages('baz', headers=[
377
367
                'Subject', 'Date', 'X-Launchpad-Message-Rationale'])
378
368
        first_message = response['messages'][0]
381
371
 
382
372
    def test_get_messages_headers_include_headers(self):
383
373
        client = GrackleClient('localhost', 8442)
384
 
        with ForkedFake.from_client(client,
385
 
            {'baz': [
386
 
                {'message_id': 'foo', 'headers': {'From': 'me', 'To': 'you'}}
387
 
            ]}):
 
374
        archive = {
 
375
            'baz': [
 
376
                make_message('foo', headers={'From': 'me', 'To': 'you'})]}
 
377
        with ForkedFakeService.from_client(client, archive):
388
378
            response = client.get_messages('baz', headers=[
389
379
                'From', 'To'])
390
380
        first_message = response['messages'][0]
393
383
 
394
384
    def test_get_messages_max_body_length(self):
395
385
        client = GrackleClient('localhost', 8443)
396
 
        with ForkedFake.from_client(client,
397
 
            {'baz': [
398
 
                {'message_id': 'foo', 'body': u'abcdefghi'}
399
 
            ]}):
 
386
        archive = {'baz': [make_message('foo', body=u'abcdefghi')]}
 
387
        with ForkedFakeService.from_client(client, archive):
400
388
            response = client.get_messages('baz', max_body_length=3)
401
389
        first_message = response['messages'][0]
402
390
        self.assertEqual('abc', first_message['body'])
403
391
 
404
392
    def test_include_hidden(self):
405
393
        client = GrackleClient('localhost', 8444)
406
 
        with ForkedFake.from_client(client,
407
 
            {'baz': [
408
 
                {'message_id': 'foo', 'hidden': True},
409
 
                {'message_id': 'bar', 'hidden': False}
410
 
            ]}):
 
394
        archive = {
 
395
            'baz': [
 
396
                make_message('foo', hidden=True),
 
397
                make_message('bar', hidden=False),
 
398
            ]}
 
399
        with ForkedFakeService.from_client(client, archive):
411
400
            response = client.get_messages('baz', include_hidden=True)
412
401
            self.assertMessageIDs(['bar', 'foo'], response['messages'])
413
402
            response = client.get_messages('baz', include_hidden=False)
414
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'])