~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:
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
6
import logging
10
7
import os
14
11
import sys
15
12
from unittest import TestCase
16
13
from urlparse import urlparse
 
14
from urlparse import parse_qs
17
15
 
18
16
from testtools import ExpectedException
19
17
 
20
18
from grackle.client import (
21
19
    GrackleClient,
22
 
    UnparsableDateRange,
23
 
    UnsupportedDisplayType,
24
20
    UnsupportedOrder,
25
21
    )
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:
 
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:
64
121
    """A Grackle service fake, as a ContextManager."""
65
122
 
66
 
    def __init__(self, port, message_archives=None, write_logs=False):
 
123
    def __init__(self, port, messages=None, write_logs=False):
67
124
        """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.
 
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.
73
129
        :param write_logs: If true, log messages will be written to stdout.
74
130
        """
75
131
        self.pid = None
76
132
        self.port = port
77
 
        if message_archives is None:
78
 
            self.message_archives = {}
 
133
        if messages is None:
 
134
            self.messages = {}
79
135
        else:
80
 
            self.message_archives = message_archives
 
136
            self.messages = messages
81
137
        self.read_end, self.write_end = os.pipe()
82
138
        self.write_logs = write_logs
83
139
 
84
140
    @staticmethod
85
 
    def from_client(client, message_archives=None):
86
 
        """Instantiate a ForkedFakeService from the client.
 
141
    def from_client(client, messages=None):
 
142
        """Instantiate a ForkedFake from the client.
87
143
 
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.
 
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.
92
148
        """
93
 
        return ForkedFakeService(client.port, message_archives)
 
149
        return ForkedFake(client.port, messages)
94
150
 
95
151
    def is_ready(self):
96
152
        """Tell the parent process that the server is ready for writes."""
111
167
    def start_server(self):
112
168
        """Start the HTTP server."""
113
169
        service = HTTPServer(('', self.port), FakeGrackleRequestHandler)
114
 
        service.store = MemoryStore(self.message_archives)
115
 
        for archive_id, messages in service.store.message_archives.iteritems():
 
170
        service.store = GrackleStore(self.messages)
 
171
        for archive_id, messages in service.store.messages.iteritems():
116
172
            for message in messages:
117
173
                message.setdefault('headers', {})
118
174
        self.is_ready()
125
181
        os.kill(self.pid, SIGKILL)
126
182
 
127
183
 
 
184
SUPPORTED_ORDERS = set(
 
185
    ['date', 'author', 'subject', 'thread_newest', 'thread_oldest',
 
186
     'thread_subject'])
 
187
 
 
188
 
128
189
class FakeGrackleRequestHandler(BaseHTTPRequestHandler):
129
190
    """A request handler that forwards to server.store."""
130
191
 
133
194
        self.logger = logging.getLogger('http')
134
195
        BaseHTTPRequestHandler.__init__(self, *args, **kwargs)
135
196
 
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) == 3:
145
 
            # This expected path is /archive/archive_id.
146
 
            try:
147
 
                self.server.store.put_archive(parts[2])
148
 
                self.send_response(httplib.CREATED)
149
 
                self.end_headers()
150
 
                self.wfile.close()
151
 
            except:
152
 
                self.send_error(httplib.BAD_REQUEST)
153
 
        if len(parts) == 4:
154
 
            # This expected path is /archive/archive_id/message_id.
155
 
            try:
156
 
                message = self.rfile.read(int(self.headers['content-length']))
157
 
                self.server.store.put_message(parts[2], parts[3], message)
158
 
                self.send_response(httplib.CREATED)
159
 
                self.end_headers()
160
 
                self.wfile.close()
161
 
            except:
162
 
                self.send_error(httplib.BAD_REQUEST)
163
 
 
164
197
    def do_POST(self):
165
 
        """Change a message on POST."""
166
 
        scheme, netloc, path, params, query_string, fragments = (
167
 
            urlparse(self.path))
168
 
        parts = path.split('/')
169
 
        if parts[1] != 'archive':
170
 
            # This is an unknonwn operation?
171
 
            return
172
 
        if len(parts) == 4:
173
 
            # This expected path is /archive/archive_id/message_id.
174
 
            try:
175
 
                # This expected path is /archive/archive_id/message_id.
176
 
                response = self.server.store.hide_message(
177
 
                    parts[2], parts[3], query_string)
178
 
                self.send_response(httplib.OK)
179
 
                self.end_headers()
180
 
                self.wfile.write(simplejson.dumps(response))
181
 
            except:
182
 
                self.send_error(httplib.BAD_REQUEST)
 
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)
183
206
 
184
207
    def do_GET(self):
185
208
        """Retrieve a list of messages on GET."""
193
216
                self.send_response(httplib.OK)
194
217
                self.end_headers()
195
218
                self.wfile.write(simplejson.dumps(response))
196
 
            except Exception, error:
197
 
                self.send_response(
198
 
                    httplib.BAD_REQUEST, error.__doc__)
 
219
            except UnsupportedOrder:
 
220
                self.send_response(httplib.BAD_REQUEST)
 
221
                self.wfile.write('Unsupported order')
199
222
                return
200
223
 
201
224
    def log_message(self, format, *args):
205
228
        self.logger.info(message)
206
229
 
207
230
 
208
 
class TestPutArchive(TestCase):
209
 
 
210
 
    def test_put_message(self):
211
 
        client = GrackleClient('localhost', 8410)
212
 
        message_archives = {}
213
 
        with ForkedFakeService.from_client(client, message_archives):
214
 
            client.put_archive('arch1')
215
 
            response = client.get_messages('arch1')
216
 
        self.assertEqual(0, len(response['messages']))
217
 
 
218
 
 
219
231
class TestPutMessage(TestCase):
220
232
 
221
233
    def test_put_message(self):
222
 
        client = GrackleClient('localhost', 8420)
223
 
        message_archives = {'arch1': []}
224
 
        with ForkedFakeService.from_client(client, message_archives):
225
 
            client.put_message('arch1', 'id1', StringIO('This is a message'))
226
 
            response = client.get_messages('arch1')
227
 
        self.assertEqual(1, len(response['messages']))
228
 
        message = response['messages'][0]
229
 
        self.assertEqual('id1', message['message_id'])
230
 
 
231
 
    def test_put_message_without_archive(self):
232
 
        client = GrackleClient('localhost', 8421)
233
 
        message_archives = {'arch1': []}
234
 
        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'))
235
237
            with ExpectedException(Exception, 'wtf'):
236
 
                client.put_message('no-archive', 'id1', StringIO('message'))
 
238
                client.put_message('arch1', 'asdf',
 
239
                    StringIO('This is not a message'))
237
240
 
238
241
 
239
242
class TestGetMessages(TestCase):
246
249
            sorted(ids), sorted(messages, key=lambda m: m['message_id']))
247
250
 
248
251
    def test_get_messages(self):
249
 
        client = GrackleClient('localhost', 8430)
250
 
        archive = {
251
 
            'baz': [make_message('foo'), make_message('bar')]}
252
 
        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'}]}):
253
257
            response = client.get_messages('baz')
254
258
        self.assertEqual(['bar', 'foo'], sorted(m['message_id'] for m in
255
259
            response['messages']))
258
262
 
259
263
    def test_get_messages_by_id(self):
260
264
        client = GrackleClient('localhost', 8437)
261
 
        archive = {
262
 
            'baz': [make_message('foo'), make_message('bar')]}
263
 
        with ForkedFakeService.from_client(client, archive):
 
265
        with ForkedFake.from_client(client,
 
266
            {'baz':
 
267
            [{'message_id': 'foo'},
 
268
             {'message_id': 'bar'}]}):
264
269
            response = client.get_messages('baz', message_ids=['foo'])
265
270
        message, = response['messages']
266
271
        self.assertEqual('foo', message['message_id'])
267
272
 
268
273
    def test_get_messages_batching(self):
269
274
        client = GrackleClient('localhost', 8438)
270
 
        archive = {'baz': [make_message('foo'), make_message('bar')]}
271
 
        with ForkedFakeService.from_client(client, archive):
 
275
        with ForkedFake.from_client(client,
 
276
            {'baz':
 
277
            [{'message_id': 'foo'},
 
278
             {'message_id': 'bar'}]}):
272
279
            response = client.get_messages('baz', limit=1)
273
280
            self.assertEqual(1, len(response['messages']))
274
281
            messages = response['messages']
280
287
 
281
288
    def get_messages_member_order_test(self, key):
282
289
        client = GrackleClient('localhost', 8439)
283
 
        if key == 'author':
284
 
            header_name = 'from'
285
 
        else:
286
 
            header_name = key
287
 
        archive = {
288
 
            'baz': [
289
 
                make_message('foo', headers={header_name: '2011-03-25'}),
290
 
                make_message('bar', headers={header_name: '2011-03-24'}),
291
 
             ]}
292
 
        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'}]}):
293
293
            response = client.get_messages('baz')
294
294
            self.assertIDOrder(['foo', 'bar'], response['messages'])
295
295
            response = client.get_messages('baz', order=key)
305
305
        self.get_messages_member_order_test('subject')
306
306
 
307
307
    def test_get_messages_thread_subject_order(self):
308
 
        archive = {
309
 
            'baz': [
310
 
                make_message('bar', headers={'subject': 'y'}),
311
 
                make_message('qux', headers={'subject': 'z'}),
312
 
                make_message('foo', headers={'subject': 'x',
313
 
                                             'in-reply-to': 'qux'}),
314
 
             ]}
315
308
        client = GrackleClient('localhost', 8439)
316
 
        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
            ]}):
317
314
            response = client.get_messages('baz')
318
315
            self.assertIDOrder(['bar', 'qux', 'foo'], response['messages'])
319
316
            response = client.get_messages('baz', order='subject')
323
320
 
324
321
    def test_get_messages_thread_oldest_order(self):
325
322
        client = GrackleClient('localhost', 8439)
326
 
        archive = {
327
 
            'baz': [
328
 
                make_message('bar', headers={'date': 'x'}),
329
 
                make_message('qux', headers={'date': 'z'}),
330
 
                make_message('foo', headers={'date': 'y',
331
 
                                             'in-reply-to': 'qux'}),
332
 
            ]}
333
 
        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
            ]}):
334
328
            response = client.get_messages('baz')
335
329
            self.assertIDOrder(['bar', 'qux', 'foo'], response['messages'])
336
330
            response = client.get_messages('baz', order='date')
340
334
 
341
335
    def test_get_messages_thread_newest_order(self):
342
336
        client = GrackleClient('localhost', 8439)
343
 
        archive = {
344
 
            'baz': [
345
 
                make_message('bar', headers={'date': 'x'}),
346
 
                make_message('qux', headers={'date': 'w'}),
347
 
                make_message('foo', headers={'date': 'y',
348
 
                                             'in-reply-to': 'bar'}),
349
 
                make_message('baz', headers={'date': 'z',
350
 
                                             'in-reply-to': 'qux'}),
351
 
            ]}
352
 
        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
            ]}):
353
343
            response = client.get_messages('baz', order='date')
354
344
            self.assertIDOrder(
355
345
                ['qux', 'bar', 'foo', 'baz'], response['messages'])
359
349
 
360
350
    def test_get_messages_unsupported_order(self):
361
351
        client = GrackleClient('localhost', 8439)
362
 
        archive = {
363
 
            'baz': [
364
 
                make_message('foo', headers={'date': '2011-03-25'}),
365
 
                make_message('foo', headers={'date': '2011-03-24'}),
366
 
            ]}
367
 
        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'}]}):
368
355
            with ExpectedException(UnsupportedOrder, ''):
369
356
                client.get_messages('baz', order='nonsense')
370
357
 
371
358
    def test_get_messages_headers_no_headers(self):
372
359
        client = GrackleClient('localhost', 8440)
373
 
        archive = {'baz': [make_message('foo')]}
374
 
        with ForkedFakeService.from_client(client, archive):
 
360
        with ForkedFake.from_client(client,
 
361
            {'baz': [
 
362
                {'message_id': 'foo'}
 
363
            ]}):
375
364
            response = client.get_messages('baz', headers=[
376
365
                'Subject', 'Date', 'X-Launchpad-Message-Rationale'])
377
366
        first_message = response['messages'][0]
380
369
 
381
370
    def test_get_messages_headers_exclude_headers(self):
382
371
        client = GrackleClient('localhost', 8441)
383
 
        archive = {
384
 
            'baz': [make_message('foo', headers={'From': 'me'})]}
385
 
        with ForkedFakeService.from_client(client, archive):
 
372
        with ForkedFake.from_client(client,
 
373
            {'baz': [
 
374
                {'message_id': 'foo', 'headers': {'From': 'me'}}
 
375
            ]}):
386
376
            response = client.get_messages('baz', headers=[
387
377
                'Subject', 'Date', 'X-Launchpad-Message-Rationale'])
388
378
        first_message = response['messages'][0]
391
381
 
392
382
    def test_get_messages_headers_include_headers(self):
393
383
        client = GrackleClient('localhost', 8442)
394
 
        archive = {
395
 
            'baz': [
396
 
                make_message('foo', headers={'From': 'me', 'To': 'you'})]}
397
 
        with ForkedFakeService.from_client(client, archive):
 
384
        with ForkedFake.from_client(client,
 
385
            {'baz': [
 
386
                {'message_id': 'foo', 'headers': {'From': 'me', 'To': 'you'}}
 
387
            ]}):
398
388
            response = client.get_messages('baz', headers=[
399
389
                'From', 'To'])
400
390
        first_message = response['messages'][0]
403
393
 
404
394
    def test_get_messages_max_body_length(self):
405
395
        client = GrackleClient('localhost', 8443)
406
 
        archive = {'baz': [make_message('foo', body=u'abcdefghi')]}
407
 
        with ForkedFakeService.from_client(client, archive):
 
396
        with ForkedFake.from_client(client,
 
397
            {'baz': [
 
398
                {'message_id': 'foo', 'body': u'abcdefghi'}
 
399
            ]}):
408
400
            response = client.get_messages('baz', max_body_length=3)
409
401
        first_message = response['messages'][0]
410
402
        self.assertEqual('abc', first_message['body'])
411
403
 
412
404
    def test_include_hidden(self):
413
405
        client = GrackleClient('localhost', 8444)
414
 
        archive = {
415
 
            'baz': [
416
 
                make_message('foo', hidden=True),
417
 
                make_message('bar', hidden=False),
418
 
            ]}
419
 
        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
            ]}):
420
411
            response = client.get_messages('baz', include_hidden=True)
421
412
            self.assertMessageIDs(['bar', 'foo'], response['messages'])
422
413
            response = client.get_messages('baz', include_hidden=False)
423
414
            self.assertMessageIDs(['bar'], response['messages'])
424
 
 
425
 
    def test_display_type_unknown_value(self):
426
 
        client = GrackleClient('localhost', 8445)
427
 
        archive = {'baz': [make_message('foo', body=u'abcdefghi')]}
428
 
        with ForkedFakeService.from_client(client, archive):
429
 
            with ExpectedException(UnsupportedDisplayType, ''):
430
 
                client.get_messages('baz', display_type='unknown')
431
 
 
432
 
    def test_display_type_headers_only(self):
433
 
        client = GrackleClient('localhost', 8446)
434
 
        archive = {
435
 
            'baz': [
436
 
                make_message('foo', body=u'abcdefghi',
437
 
                             headers={'From': 'me', 'To': 'you'})]}
438
 
        with ForkedFakeService.from_client(client, archive):
439
 
            response = client.get_messages('baz', display_type='headers-only')
440
 
        first_message = response['messages'][0]
441
 
        self.assertEqual('foo', first_message['message_id'])
442
 
        self.assertEqual(
443
 
            archive['baz'][0]['headers'], first_message['headers'])
444
 
        self.assertNotIn('body', first_message)
445
 
 
446
 
    def test_display_type_text_only(self):
447
 
        client = GrackleClient('localhost', 8446)
448
 
        archive = {
449
 
            'baz': [
450
 
                make_mime_message(
451
 
                    'foo', 'abcdefghi',
452
 
                    headers={'From': 'me', 'To': 'you'},
453
 
                    attachment_type='text/x-diff')]}
454
 
        with ForkedFakeService.from_client(client, archive):
455
 
            response = client.get_messages('baz', display_type='text-only')
456
 
        first_message = response['messages'][0]
457
 
        self.assertEqual('foo', first_message['message_id'])
458
 
        self.assertEqual('me', first_message['headers']['From'])
459
 
        self.assertEqual('you', first_message['headers']['To'])
460
 
        self.assertEqual(archive['baz'][0]['body'], first_message['body'])
461
 
 
462
 
    def test_display_type_all(self):
463
 
        client = GrackleClient('localhost', 8447)
464
 
        archive = {
465
 
            'baz': [
466
 
                make_mime_message(
467
 
                    'foo', 'abcdefghi',
468
 
                    headers={'From': 'me', 'To': 'you'},
469
 
                    attachment_type='text/x-diff')]}
470
 
        with ForkedFakeService.from_client(client, archive):
471
 
            response = client.get_messages('baz', display_type='all')
472
 
        first_message = response['messages'][0]
473
 
        self.assertEqual('foo', first_message['message_id'])
474
 
        self.assertEqual('me', first_message['headers']['From'])
475
 
        self.assertEqual('you', first_message['headers']['To'])
476
 
        self.assertEqual(archive['baz'][0]['body'], first_message['body'])
477
 
 
478
 
    def test_date_range(self):
479
 
        client = GrackleClient('localhost', 8448)
480
 
        archive = {
481
 
            'baz': [
482
 
                make_mime_message(
483
 
                    'foo', 'abcdefghi', headers={'date': '2011-12-31'}),
484
 
                make_mime_message(
485
 
                    'bar', 'abcdefghi', headers={'date': '2012-01-01'}),
486
 
                make_mime_message(
487
 
                    'qux', 'abcdefghi', headers={'date': '2012-01-15'}),
488
 
                make_mime_message(
489
 
                    'naf', 'abcdefghi', headers={'date': '2012-01-31'}),
490
 
                make_mime_message(
491
 
                    'doh', 'abcdefghi', headers={'date': '2012-02-01'}),
492
 
                    ]}
493
 
        with ForkedFakeService.from_client(client, archive):
494
 
            response = client.get_messages(
495
 
                'baz', date_range='2012-01-01..2012-01-31')
496
 
        ids = sorted(m['message_id'] for m in response['messages'])
497
 
        self.assertEqual(['bar', 'naf', 'qux'], ids)
498
 
 
499
 
    def test_date_range_unparsabledaterange(self):
500
 
        client = GrackleClient('localhost', 8449)
501
 
        archive = {'baz': [make_message('foo', body=u'abcdefghi')]}
502
 
        with ForkedFakeService.from_client(client, archive):
503
 
            with ExpectedException(UnparsableDateRange, ''):
504
 
                client.get_messages('baz', date_range='2012-01-01')
505
 
 
506
 
    def test_date_range_unparsabledaterange_missing_part(self):
507
 
        client = GrackleClient('localhost', 8450)
508
 
        archive = {'baz': [make_message('foo', body=u'abcdefghi')]}
509
 
        with ForkedFakeService.from_client(client, archive):
510
 
            with ExpectedException(UnparsableDateRange, ''):
511
 
                client.get_messages('baz', date_range='2012-01-01..')
512
 
 
513
 
    def test_date_range_unparsabledaterange_extra_part(self):
514
 
        client = GrackleClient('localhost', 8451)
515
 
        archive = {'baz': [make_message('foo', body=u'abcdefghi')]}
516
 
        with ForkedFakeService.from_client(client, archive):
517
 
            with ExpectedException(UnparsableDateRange, ''):
518
 
                client.get_messages('baz', date_range='2012-01..12-02..12-03')
519
 
 
520
 
 
521
 
class TestHideMessages(TestCase):
522
 
 
523
 
    def test_hide_message_true(self):
524
 
        client = GrackleClient('localhost', 8470)
525
 
        archive = {
526
 
            'baz': [
527
 
                make_message('foo', hidden=False),
528
 
            ]}
529
 
        with ForkedFakeService.from_client(client, archive):
530
 
            response = client.hide_message('baz', 'foo', hidden=True)
531
 
        self.assertEqual('foo', response['message_id'])
532
 
        self.assertIs(True, response['hidden'])
533
 
 
534
 
    def test_hide_message_false(self):
535
 
        client = GrackleClient('localhost', 8470)
536
 
        archive = {
537
 
            'baz': [
538
 
                make_message('foo', hidden=True),
539
 
            ]}
540
 
        with ForkedFakeService.from_client(client, archive):
541
 
            response = client.hide_message('baz', 'foo', hidden=False)
542
 
        self.assertEqual('foo', response['message_id'])
543
 
        self.assertIs(False, response['hidden'])