~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 20:25:49 UTC
  • Revision ID: curtis.hovey@canonical.com-20120316202549-2dqmxc2f8d9dr7k9
Raise an error when the archive already exists.

Show diffs side-by-side

added added

removed removed

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