~didrocks/unity/altf10

« back to all changes in this revision

Viewing changes to grackle/tests/test_client.py

  • Committer: William Grant
  • Date: 2012-01-25 06:19:56 UTC
  • Revision ID: william.grant@canonical.com-20120125061956-4tltjt6a4xf5yufj
Fix test.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
__metaclass__ = type
2
 
 
3
 
from email.message import Message
4
 
from email.mime.multipart import MIMEMultipart
5
 
from email.mime.text import MIMEText
 
1
from BaseHTTPServer import (
 
2
    HTTPServer,
 
3
    BaseHTTPRequestHandler,
 
4
    )
 
5
import httplib
 
6
import logging
 
7
import os
 
8
from signal import SIGKILL
 
9
import simplejson
6
10
from StringIO import StringIO
 
11
import sys
7
12
from unittest import TestCase
 
13
from urlparse import urlparse
 
14
from urlparse import parse_qs
8
15
 
9
16
from testtools import ExpectedException
10
17
 
11
 
from grackle.client import GrackleClient
12
 
from grackle.error import (
13
 
    ArchiveIdExists,
14
 
    UnparsableDateRange,
15
 
    UnsupportedDisplayType,
 
18
from grackle.client import (
 
19
    GrackleClient,
16
20
    UnsupportedOrder,
17
21
    )
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):
23
 
    if headers is None:
24
 
        headers = {}
25
 
    message_headers = {
26
 
        'Message-Id': message_id,
27
 
        'date': '2005-01-01',
28
 
        'subject': 'subject',
29
 
        'from': 'author',
30
 
        'replies': '',
31
 
        }
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,
41
 
                      attachment_type=None):
42
 
    parts = MIMEMultipart()
43
 
    parts.attach(MIMEText(body))
44
 
    if attachment_type is not None:
45
 
        attachment = Message()
46
 
        attachment.set_payload('attactment data.')
47
 
        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')
 
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 (
 
76
                not parameters['include_hidden']
 
77
                and message.get('hidden', False)):
 
78
                continue
 
79
 
 
80
            if ('message_ids' in parameters and
 
81
                message['message_id'] not in parameters['message_ids']):
 
82
                continue
 
83
            message = dict(message)
 
84
            if 'headers' in parameters:
 
85
                headers = dict(
 
86
                    (k, v) for k, v in message['headers'].iteritems()
 
87
                    if k in parameters['headers'])
 
88
                message['headers'] = headers
 
89
            max_body = parameters.get('max_body_length')
 
90
            if max_body is not None:
 
91
                message['body'] = message['body'][:max_body]
 
92
            new_messages.append(message)
 
93
        messages = new_messages
 
94
        limit = parameters.get('limit', 100)
 
95
        memo = parameters.get('memo')
 
96
        message_id_indices = dict(
 
97
            (m['message_id'], idx) for idx, m in enumerate(messages))
 
98
        if memo is None:
 
99
            start = 0
 
100
        else:
 
101
            start = message_id_indices[memo.encode('rot13')]
 
102
        if start > 0:
 
103
            previous_memo = messages[start - 1]['message_id'].encode('rot13')
 
104
        else:
 
105
            previous_memo = None
 
106
        end = min(start + limit, len(messages))
 
107
        if end < len(messages):
 
108
            next_memo = messages[end]['message_id'].encode('rot13')
 
109
        else:
 
110
            next_memo = None
 
111
        messages = messages[start:end]
 
112
 
 
113
        response = {
 
114
            'messages': messages,
 
115
            'next_memo': next_memo,
 
116
            'previous_memo': previous_memo
 
117
            }
 
118
        return response
 
119
 
 
120
 
 
121
 
 
122
class ForkedFake:
 
123
    """A Grackle service fake, as a ContextManager."""
 
124
 
 
125
    def __init__(self, port, messages=None, write_logs=False):
 
126
        """Constructor.
 
127
        :param port: The tcp port to use
 
128
        :param messages: A dict of lists of dicts representing messages.  The
 
129
            outer dict represents the archive, the list represents the list of
 
130
            messages for that archive.
 
131
        :param write_logs: If true, log messages will be written to stdout.
 
132
        """
 
133
        self.pid = None
 
134
        self.port = port
 
135
        if messages is None:
 
136
            self.messages = {}
 
137
        else:
 
138
            self.messages = messages
 
139
        self.read_end, self.write_end = os.pipe()
 
140
        self.write_logs = write_logs
 
141
 
 
142
    @staticmethod
 
143
    def from_client(client, messages=None):
 
144
        """Instantiate a ForkedFake from the client.
 
145
 
 
146
        :param port: The client  to provide service for.
 
147
        :param messages: A dict of lists of dicts representing messages.  The
 
148
            outer dict represents the archive, the list represents the list of
 
149
            messages for that archive.
 
150
        """
 
151
        return ForkedFake(client.port, messages)
 
152
 
 
153
    def is_ready(self):
 
154
        """Tell the parent process that the server is ready for writes."""
 
155
        os.write(self.write_end, 'asdf')
 
156
 
 
157
    def __enter__(self):
 
158
        """Run the service.
 
159
 
 
160
        Fork and start a server in the child.  Return when the server is ready
 
161
        for use."""
 
162
        pid = os.fork()
 
163
        if pid == 0:
 
164
            self.start_server()
 
165
        self.pid = pid
 
166
        os.read(self.read_end, 1)
 
167
        return
 
168
 
 
169
    def start_server(self):
 
170
        """Start the HTTP server."""
 
171
        service = HTTPServer(('', self.port), FakeGrackleRequestHandler)
 
172
        service.store = GrackleStore(self.messages)
 
173
        for archive_id, messages in service.store.messages.iteritems():
 
174
            for message in messages:
 
175
                message.setdefault('headers', {})
 
176
        self.is_ready()
 
177
        if self.write_logs:
 
178
            logging.basicConfig(
 
179
                stream=sys.stderr, level=logging.INFO)
 
180
        service.serve_forever()
 
181
 
 
182
    def __exit__(self, exc_type, exc_val, traceback):
 
183
        os.kill(self.pid, SIGKILL)
 
184
 
 
185
 
 
186
SUPPORTED_ORDERS = set(
 
187
    ['date', 'author', 'subject', 'thread_newest', 'thread_oldest',
 
188
     'thread_subject'])
 
189
 
 
190
 
 
191
class FakeGrackleRequestHandler(BaseHTTPRequestHandler):
 
192
    """A request handler that forwards to server.store."""
 
193
 
 
194
    def __init__(self, *args, **kwargs):
 
195
        """Constructor.  Sets up logging."""
 
196
        self.logger = logging.getLogger('http')
 
197
        BaseHTTPRequestHandler.__init__(self, *args, **kwargs)
 
198
 
 
199
    def do_POST(self):
 
200
        """Create a message on POST."""
 
201
        message = self.rfile.read(int(self.headers['content-length']))
 
202
        if message == 'This is a message':
 
203
            self.send_response(httplib.CREATED)
 
204
            self.end_headers()
 
205
            self.wfile.close()
 
206
        else:
 
207
            self.send_error(httplib.BAD_REQUEST)
 
208
 
 
209
    def do_GET(self):
 
210
        """Retrieve a list of messages on GET."""
 
211
        scheme, netloc, path, params, query_string, fragments = (
 
212
            urlparse(self.path))
 
213
        parts = path.split('/')
 
214
        if parts[1] == 'archive':
 
215
            try:
 
216
                response = self.server.store.get_messages(
 
217
                    parts[2], query_string)
 
218
                self.send_response(httplib.OK)
 
219
                self.end_headers()
 
220
                self.wfile.write(simplejson.dumps(response))
 
221
            except UnsupportedOrder:
 
222
                self.send_response(httplib.BAD_REQUEST)
 
223
                self.wfile.write('Unsupported order')
 
224
                return
 
225
 
 
226
    def log_message(self, format, *args):
 
227
        """Override log_message to use standard Python logging."""
 
228
        message = "%s - - [%s] %s\n" % (
 
229
            self.address_string(), self.log_date_time_string(), format%args)
 
230
        self.logger.info(message)
69
231
 
70
232
 
71
233
class TestPutMessage(TestCase):
72
234
 
73
235
    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):
 
236
        client = GrackleClient('localhost', 8436)
 
237
        with ForkedFake.from_client(client):
 
238
            client.put_message('arch1', 'asdf', StringIO('This is a message'))
87
239
            with ExpectedException(Exception, 'wtf'):
88
 
                client.put_message('no-archive', 'id1', StringIO('message'))
 
240
                client.put_message('arch1', 'asdf',
 
241
                    StringIO('This is not a message'))
89
242
 
90
243
 
91
244
class TestGetMessages(TestCase):
95
248
 
96
249
    def assertMessageIDs(self, ids, messages):
97
250
        self.assertIDOrder(
98
 
            sorted(ids), sorted(messages, key=lambda m: m['message_id']))
 
251
            sorted(ids), sorted(messages, key=lambda m:m['message_id']))
99
252
 
100
253
    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):
 
254
        client = GrackleClient('localhost', 8435)
 
255
        with ForkedFake.from_client(client,
 
256
            {'baz':
 
257
            [{'message_id': 'foo'},
 
258
             {'message_id': 'bar'}]}):
105
259
            response = client.get_messages('baz')
106
260
        self.assertEqual(['bar', 'foo'], sorted(m['message_id'] for m in
107
261
            response['messages']))
110
264
 
111
265
    def test_get_messages_by_id(self):
112
266
        client = GrackleClient('localhost', 8437)
113
 
        archive = {
114
 
            'baz': [make_message('foo'), make_message('bar')]}
115
 
        with ForkedFakeService.from_client(client, archive):
 
267
        with ForkedFake.from_client(client,
 
268
            {'baz':
 
269
            [{'message_id': 'foo'},
 
270
             {'message_id': 'bar'}]}):
116
271
            response = client.get_messages('baz', message_ids=['foo'])
117
272
        message, = response['messages']
118
273
        self.assertEqual('foo', message['message_id'])
119
274
 
120
275
    def test_get_messages_batching(self):
121
276
        client = GrackleClient('localhost', 8438)
122
 
        archive = {'baz': [make_message('foo'), make_message('bar')]}
123
 
        with ForkedFakeService.from_client(client, archive):
 
277
        with ForkedFake.from_client(client,
 
278
            {'baz':
 
279
            [{'message_id': 'foo'},
 
280
             {'message_id': 'bar'}]}):
124
281
            response = client.get_messages('baz', limit=1)
125
282
            self.assertEqual(1, len(response['messages']))
126
283
            messages = response['messages']
132
289
 
133
290
    def get_messages_member_order_test(self, key):
134
291
        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):
 
292
        with ForkedFake.from_client(client,
 
293
                {'baz': [{'message_id': 'foo', key: '2011-03-25'},
 
294
                 {'message_id': 'bar', key: '2011-03-24'}]}):
145
295
            response = client.get_messages('baz')
146
296
            self.assertIDOrder(['foo', 'bar'], response['messages'])
147
297
            response = client.get_messages('baz', order=key)
157
307
        self.get_messages_member_order_test('subject')
158
308
 
159
309
    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
 
             ]}
167
310
        client = GrackleClient('localhost', 8439)
168
 
        with ForkedFakeService.from_client(client, archive):
 
311
        with ForkedFake.from_client(client, {'baz': [
 
312
            {'message_id': 'bar', 'subject': 'y'},
 
313
            {'message_id': 'qux', 'subject': 'z'},
 
314
            {'message_id': 'foo', 'subject': 'x', 'in_reply_to': 'qux'},
 
315
            ]}):
169
316
            response = client.get_messages('baz')
170
317
            self.assertIDOrder(['bar', 'qux', 'foo'], response['messages'])
171
318
            response = client.get_messages('baz', order='subject')
175
322
 
176
323
    def test_get_messages_thread_oldest_order(self):
177
324
        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'}),
184
 
            ]}
185
 
        with ForkedFakeService.from_client(client, archive):
 
325
        with ForkedFake.from_client(client, {'baz': [
 
326
            {'message_id': 'bar', 'date': 'x'},
 
327
            {'message_id': 'qux', 'date': 'z'},
 
328
            {'message_id': 'foo', 'date': 'y', 'in_reply_to': 'qux'},
 
329
            ]}):
186
330
            response = client.get_messages('baz')
187
331
            self.assertIDOrder(['bar', 'qux', 'foo'], response['messages'])
188
332
            response = client.get_messages('baz', order='date')
192
336
 
193
337
    def test_get_messages_thread_newest_order(self):
194
338
        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):
 
339
        with ForkedFake.from_client(client, {'baz': [
 
340
            {'message_id': 'bar', 'date': 'x'},
 
341
            {'message_id': 'qux', 'date': 'w'},
 
342
            {'message_id': 'foo', 'date': 'y', 'in_reply_to': 'bar'},
 
343
            {'message_id': 'baz', 'date': 'z', 'in_reply_to': 'qux'},
 
344
            ]}):
205
345
            response = client.get_messages('baz', order='date')
206
346
            self.assertIDOrder(
207
347
                ['qux', 'bar', 'foo', 'baz'], response['messages'])
211
351
 
212
352
    def test_get_messages_unsupported_order(self):
213
353
        client = GrackleClient('localhost', 8439)
214
 
        archive = {
215
 
            'baz': [
216
 
                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):
 
354
        with ForkedFake.from_client(client,
 
355
                {'baz': [{'message_id': 'foo', 'date': '2011-03-25'},
 
356
                 {'message_id': 'bar', 'date': '2011-03-24'}]}):
220
357
            with ExpectedException(UnsupportedOrder, ''):
221
358
                client.get_messages('baz', order='nonsense')
222
359
 
223
360
    def test_get_messages_headers_no_headers(self):
224
361
        client = GrackleClient('localhost', 8440)
225
 
        archive = {'baz': [make_message('foo')]}
226
 
        with ForkedFakeService.from_client(client, archive):
 
362
        with ForkedFake.from_client(client,
 
363
            {'baz': [
 
364
                {'message_id': 'foo'}
 
365
            ]}):
227
366
            response = client.get_messages('baz', headers=[
228
367
                'Subject', 'Date', 'X-Launchpad-Message-Rationale'])
229
368
        first_message = response['messages'][0]
232
371
 
233
372
    def test_get_messages_headers_exclude_headers(self):
234
373
        client = GrackleClient('localhost', 8441)
235
 
        archive = {
236
 
            'baz': [make_message('foo', headers={'From': 'me'})]}
237
 
        with ForkedFakeService.from_client(client, archive):
 
374
        with ForkedFake.from_client(client,
 
375
            {'baz': [
 
376
                {'message_id': 'foo', 'headers': {'From': 'me'}}
 
377
            ]}):
238
378
            response = client.get_messages('baz', headers=[
239
379
                'Subject', 'Date', 'X-Launchpad-Message-Rationale'])
240
380
        first_message = response['messages'][0]
243
383
 
244
384
    def test_get_messages_headers_include_headers(self):
245
385
        client = GrackleClient('localhost', 8442)
246
 
        archive = {
247
 
            'baz': [
248
 
                make_message('foo', headers={'From': 'me', 'To': 'you'})]}
249
 
        with ForkedFakeService.from_client(client, archive):
 
386
        with ForkedFake.from_client(client,
 
387
            {'baz': [
 
388
                {'message_id': 'foo', 'headers': {'From': 'me', 'To': 'you'}}
 
389
            ]}):
250
390
            response = client.get_messages('baz', headers=[
251
391
                'From', 'To'])
252
392
        first_message = response['messages'][0]
255
395
 
256
396
    def test_get_messages_max_body_length(self):
257
397
        client = GrackleClient('localhost', 8443)
258
 
        archive = {'baz': [make_message('foo', body=u'abcdefghi')]}
259
 
        with ForkedFakeService.from_client(client, archive):
 
398
        with ForkedFake.from_client(client,
 
399
            {'baz': [
 
400
                {'message_id': 'foo', 'body': u'abcdefghi'}
 
401
            ]}):
260
402
            response = client.get_messages('baz', max_body_length=3)
261
403
        first_message = response['messages'][0]
262
404
        self.assertEqual('abc', first_message['body'])
263
405
 
264
406
    def test_include_hidden(self):
265
407
        client = GrackleClient('localhost', 8444)
266
 
        archive = {
267
 
            'baz': [
268
 
                make_message('foo', hidden=True),
269
 
                make_message('bar', hidden=False),
270
 
            ]}
271
 
        with ForkedFakeService.from_client(client, archive):
 
408
        with ForkedFake.from_client(client,
 
409
            {'baz': [
 
410
                {'message_id': 'foo', 'hidden': True},
 
411
                {'message_id': 'bar', 'hidden': False}
 
412
            ]}):
272
413
            response = client.get_messages('baz', include_hidden=True)
273
414
            self.assertMessageIDs(['bar', 'foo'], response['messages'])
274
415
            response = client.get_messages('baz', include_hidden=False)
275
416
            self.assertMessageIDs(['bar'], response['messages'])
276
417
 
277
 
    def test_display_type_unknown_value(self):
278
 
        client = GrackleClient('localhost', 8445)
279
 
        archive = {'baz': [make_message('foo', body=u'abcdefghi')]}
280
 
        with ForkedFakeService.from_client(client, archive):
281
 
            with ExpectedException(UnsupportedDisplayType, ''):
282
 
                client.get_messages('baz', display_type='unknown')
283
 
 
284
 
    def test_display_type_headers_only(self):
285
 
        client = GrackleClient('localhost', 8446)
286
 
        archive = {
287
 
            'baz': [
288
 
                make_message('foo', body=u'abcdefghi',
289
 
                             headers={'From': 'me', 'To': 'you'})]}
290
 
        with ForkedFakeService.from_client(client, archive):
291
 
            response = client.get_messages('baz', display_type='headers-only')
292
 
        first_message = response['messages'][0]
293
 
        self.assertEqual('foo', first_message['message_id'])
294
 
        self.assertEqual(
295
 
            archive['baz'][0]['headers'], first_message['headers'])
296
 
        self.assertNotIn('body', first_message)
297
 
 
298
 
    def test_display_type_text_only(self):
299
 
        client = GrackleClient('localhost', 8446)
300
 
        archive = {
301
 
            'baz': [
302
 
                make_mime_message(
303
 
                    'foo', 'abcdefghi',
304
 
                    headers={'From': 'me', 'To': 'you'},
305
 
                    attachment_type='text/x-diff')]}
306
 
        with ForkedFakeService.from_client(client, archive):
307
 
            response = client.get_messages('baz', display_type='text-only')
308
 
        first_message = response['messages'][0]
309
 
        self.assertEqual('foo', first_message['message_id'])
310
 
        self.assertEqual('me', first_message['headers']['From'])
311
 
        self.assertEqual('you', first_message['headers']['To'])
312
 
        self.assertEqual(archive['baz'][0]['body'], first_message['body'])
313
 
 
314
 
    def test_display_type_all(self):
315
 
        client = GrackleClient('localhost', 8447)
316
 
        archive = {
317
 
            'baz': [
318
 
                make_mime_message(
319
 
                    'foo', 'abcdefghi',
320
 
                    headers={'From': 'me', 'To': 'you'},
321
 
                    attachment_type='text/x-diff')]}
322
 
        with ForkedFakeService.from_client(client, archive):
323
 
            response = client.get_messages('baz', display_type='all')
324
 
        first_message = response['messages'][0]
325
 
        self.assertEqual('foo', first_message['message_id'])
326
 
        self.assertEqual('me', first_message['headers']['From'])
327
 
        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'])