~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:38:35 UTC
  • Revision ID: curtis.hovey@canonical.com-20120316193835-egu5tc1n0xlr5udj
Rename args to be honest about what is expected.

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