~didrocks/unity/altf10

« back to all changes in this revision

Viewing changes to grackle/tests/test_client.py

  • Committer: Curtis Hovey
  • Date: 2012-01-31 04:27:13 UTC
  • mto: This revision was merged to the branch mainline in revision 37.
  • Revision ID: curtis.hovey@canonical.com-20120131042713-tsjq1nx6jtwiyw5p
Use make_message() to make test data consistent.

Show diffs side-by-side

added added

removed removed

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