~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 (not parameters['include_hidden']
76
 
                and message.get('hidden', False)):
77
 
                continue
78
 
 
79
 
            if ('message_ids' in parameters
80
 
                and message['message_id'] not in parameters['message_ids']):
81
 
                continue
82
 
            message = dict(message)
83
 
            if 'headers' in parameters:
84
 
                headers = dict(
85
 
                    (k, v) for k, v in message['headers'].iteritems()
86
 
                    if k in parameters['headers'])
87
 
                message['headers'] = headers
88
 
            max_body = parameters.get('max_body_length')
89
 
            if max_body is not None:
90
 
                message['body'] = message['body'][:max_body]
91
 
            new_messages.append(message)
92
 
        messages = new_messages
93
 
        limit = parameters.get('limit', 100)
94
 
        memo = parameters.get('memo')
95
 
        message_id_indices = dict(
96
 
            (m['message_id'], idx) for idx, m in enumerate(messages))
97
 
        if memo is None:
98
 
            start = 0
99
 
        else:
100
 
            start = message_id_indices[memo.encode('rot13')]
101
 
        if start > 0:
102
 
            previous_memo = messages[start - 1]['message_id'].encode('rot13')
103
 
        else:
104
 
            previous_memo = None
105
 
        end = min(start + limit, len(messages))
106
 
        if end < len(messages):
107
 
            next_memo = messages[end]['message_id'].encode('rot13')
108
 
        else:
109
 
            next_memo = None
110
 
        messages = messages[start:end]
111
 
 
112
 
        response = {
113
 
            'messages': messages,
114
 
            'next_memo': next_memo,
115
 
            'previous_memo': previous_memo
116
 
            }
117
 
        return response
 
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)
118
62
 
119
63
 
120
64
class ForkedFakeService:
121
65
    """A Grackle service fake, as a ContextManager."""
122
66
 
123
 
    def __init__(self, port, messages=None, write_logs=False):
 
67
    def __init__(self, port, message_archives=None, write_logs=False):
124
68
        """Constructor.
125
69
 
126
70
        :param port: The tcp port to use.
127
 
        :param messages: A dict of lists of dicts representing messages.  The
128
 
            outer dict represents the archive, the list represents the list of
129
 
            messages for that archive.
 
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.
130
74
        :param write_logs: If true, log messages will be written to stdout.
131
75
        """
132
76
        self.pid = None
133
77
        self.port = port
134
 
        if messages is None:
135
 
            self.messages = {}
 
78
        if message_archives is None:
 
79
            self.message_archives = {}
136
80
        else:
137
 
            self.messages = messages
 
81
            self.message_archives = message_archives
138
82
        self.read_end, self.write_end = os.pipe()
139
83
        self.write_logs = write_logs
140
84
 
141
85
    @staticmethod
142
 
    def from_client(client, messages=None):
 
86
    def from_client(client, message_archives=None):
143
87
        """Instantiate a ForkedFakeService from the client.
144
88
 
145
89
        :param port: The client to provide service for.
146
 
        :param messages: A dict of lists of dicts representing messages.  The
147
 
            outer dict represents the archive, the list represents the list of
148
 
            messages for that archive.
 
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.
149
93
        """
150
 
        return ForkedFakeService(client.port, messages)
 
94
        return ForkedFakeService(client.port, message_archives)
151
95
 
152
96
    def is_ready(self):
153
97
        """Tell the parent process that the server is ready for writes."""
168
112
    def start_server(self):
169
113
        """Start the HTTP server."""
170
114
        service = HTTPServer(('', self.port), FakeGrackleRequestHandler)
171
 
        service.store = GrackleStore(self.messages)
172
 
        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():
173
117
            for message in messages:
174
118
                message.setdefault('headers', {})
175
119
        self.is_ready()
182
126
        os.kill(self.pid, SIGKILL)
183
127
 
184
128
 
185
 
SUPPORTED_ORDERS = set(
186
 
    ['date', 'author', 'subject', 'thread_newest', 'thread_oldest',
187
 
     'thread_subject'])
188
 
 
189
 
 
190
129
class FakeGrackleRequestHandler(BaseHTTPRequestHandler):
191
130
    """A request handler that forwards to server.store."""
192
131
 
195
134
        self.logger = logging.getLogger('http')
196
135
        BaseHTTPRequestHandler.__init__(self, *args, **kwargs)
197
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
 
198
166
    def do_POST(self):
199
 
        """Create a message on POST."""
200
 
        message = self.rfile.read(int(self.headers['content-length']))
201
 
        if message == 'This is a message':
202
 
            self.send_response(httplib.CREATED)
203
 
            self.end_headers()
204
 
            self.wfile.close()
205
 
        else:
206
 
            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)
207
185
 
208
186
    def do_GET(self):
209
187
        """Retrieve a list of messages on GET."""
217
195
                self.send_response(httplib.OK)
218
196
                self.end_headers()
219
197
                self.wfile.write(simplejson.dumps(response))
220
 
            except UnsupportedOrder:
221
 
                self.send_response(httplib.BAD_REQUEST)
222
 
                self.wfile.write('Unsupported order')
 
198
            except Exception, error:
 
199
                self.send_response(
 
200
                    httplib.BAD_REQUEST, error.__doc__)
223
201
                return
224
202
 
225
203
    def log_message(self, format, *args):
229
207
        self.logger.info(message)
230
208
 
231
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
 
232
228
class TestPutMessage(TestCase):
233
229
 
234
230
    def test_put_message(self):
235
 
        client = GrackleClient('localhost', 8436)
236
 
        with ForkedFakeService.from_client(client):
237
 
            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):
238
244
            with ExpectedException(Exception, 'wtf'):
239
 
                client.put_message('arch1', 'asdf',
240
 
                    StringIO('This is not a message'))
 
245
                client.put_message('no-archive', 'id1', StringIO('message'))
241
246
 
242
247
 
243
248
class TestGetMessages(TestCase):
250
255
            sorted(ids), sorted(messages, key=lambda m: m['message_id']))
251
256
 
252
257
    def test_get_messages(self):
253
 
        client = GrackleClient('localhost', 8435)
254
 
        with ForkedFakeService.from_client(client,
255
 
            {'baz':
256
 
            [{'message_id': 'foo'},
257
 
             {'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):
258
262
            response = client.get_messages('baz')
259
263
        self.assertEqual(['bar', 'foo'], sorted(m['message_id'] for m in
260
264
            response['messages']))
263
267
 
264
268
    def test_get_messages_by_id(self):
265
269
        client = GrackleClient('localhost', 8437)
266
 
        with ForkedFakeService.from_client(client,
267
 
            {'baz':
268
 
            [{'message_id': 'foo'},
269
 
             {'message_id': 'bar'}]}):
 
270
        archive = {
 
271
            'baz': [make_message('foo'), make_message('bar')]}
 
272
        with ForkedFakeService.from_client(client, archive):
270
273
            response = client.get_messages('baz', message_ids=['foo'])
271
274
        message, = response['messages']
272
275
        self.assertEqual('foo', message['message_id'])
273
276
 
274
277
    def test_get_messages_batching(self):
275
278
        client = GrackleClient('localhost', 8438)
276
 
        with ForkedFakeService.from_client(client,
277
 
            {'baz':
278
 
            [{'message_id': 'foo'},
279
 
             {'message_id': 'bar'}]}):
 
279
        archive = {'baz': [make_message('foo'), make_message('bar')]}
 
280
        with ForkedFakeService.from_client(client, archive):
280
281
            response = client.get_messages('baz', limit=1)
281
282
            self.assertEqual(1, len(response['messages']))
282
283
            messages = response['messages']
288
289
 
289
290
    def get_messages_member_order_test(self, key):
290
291
        client = GrackleClient('localhost', 8439)
291
 
        with ForkedFakeService.from_client(client,
292
 
                {'baz': [{'message_id': 'foo', key: '2011-03-25'},
293
 
                 {'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):
294
302
            response = client.get_messages('baz')
295
303
            self.assertIDOrder(['foo', 'bar'], response['messages'])
296
304
            response = client.get_messages('baz', order=key)
306
314
        self.get_messages_member_order_test('subject')
307
315
 
308
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
             ]}
309
324
        client = GrackleClient('localhost', 8439)
310
 
        with ForkedFakeService.from_client(client, {'baz': [
311
 
            {'message_id': 'bar', 'subject': 'y'},
312
 
            {'message_id': 'qux', 'subject': 'z'},
313
 
            {'message_id': 'foo', 'subject': 'x', 'in_reply_to': 'qux'},
314
 
            ]}):
 
325
        with ForkedFakeService.from_client(client, archive):
315
326
            response = client.get_messages('baz')
316
327
            self.assertIDOrder(['bar', 'qux', 'foo'], response['messages'])
317
328
            response = client.get_messages('baz', order='subject')
321
332
 
322
333
    def test_get_messages_thread_oldest_order(self):
323
334
        client = GrackleClient('localhost', 8439)
324
 
        with ForkedFakeService.from_client(client, {'baz': [
325
 
            {'message_id': 'bar', 'date': 'x'},
326
 
            {'message_id': 'qux', 'date': 'z'},
327
 
            {'message_id': 'foo', 'date': 'y', 'in_reply_to': 'qux'},
328
 
            ]}):
 
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):
329
343
            response = client.get_messages('baz')
330
344
            self.assertIDOrder(['bar', 'qux', 'foo'], response['messages'])
331
345
            response = client.get_messages('baz', order='date')
335
349
 
336
350
    def test_get_messages_thread_newest_order(self):
337
351
        client = GrackleClient('localhost', 8439)
338
 
        with ForkedFakeService.from_client(client, {'baz': [
339
 
            {'message_id': 'bar', 'date': 'x'},
340
 
            {'message_id': 'qux', 'date': 'w'},
341
 
            {'message_id': 'foo', 'date': 'y', 'in_reply_to': 'bar'},
342
 
            {'message_id': 'baz', 'date': 'z', 'in_reply_to': 'qux'},
343
 
            ]}):
 
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):
344
362
            response = client.get_messages('baz', order='date')
345
363
            self.assertIDOrder(
346
364
                ['qux', 'bar', 'foo', 'baz'], response['messages'])
350
368
 
351
369
    def test_get_messages_unsupported_order(self):
352
370
        client = GrackleClient('localhost', 8439)
353
 
        with ForkedFakeService.from_client(client,
354
 
                {'baz': [{'message_id': 'foo', 'date': '2011-03-25'},
355
 
                 {'message_id': 'bar', 'date': '2011-03-24'}]}):
 
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):
356
377
            with ExpectedException(UnsupportedOrder, ''):
357
378
                client.get_messages('baz', order='nonsense')
358
379
 
359
380
    def test_get_messages_headers_no_headers(self):
360
381
        client = GrackleClient('localhost', 8440)
361
 
        with ForkedFakeService.from_client(client,
362
 
            {'baz': [
363
 
                {'message_id': 'foo'}
364
 
            ]}):
 
382
        archive = {'baz': [make_message('foo')]}
 
383
        with ForkedFakeService.from_client(client, archive):
365
384
            response = client.get_messages('baz', headers=[
366
385
                'Subject', 'Date', 'X-Launchpad-Message-Rationale'])
367
386
        first_message = response['messages'][0]
370
389
 
371
390
    def test_get_messages_headers_exclude_headers(self):
372
391
        client = GrackleClient('localhost', 8441)
373
 
        with ForkedFakeService.from_client(client,
374
 
            {'baz': [
375
 
                {'message_id': 'foo', 'headers': {'From': 'me'}}
376
 
            ]}):
 
392
        archive = {
 
393
            'baz': [make_message('foo', headers={'From': 'me'})]}
 
394
        with ForkedFakeService.from_client(client, archive):
377
395
            response = client.get_messages('baz', headers=[
378
396
                'Subject', 'Date', 'X-Launchpad-Message-Rationale'])
379
397
        first_message = response['messages'][0]
382
400
 
383
401
    def test_get_messages_headers_include_headers(self):
384
402
        client = GrackleClient('localhost', 8442)
385
 
        with ForkedFakeService.from_client(client,
386
 
            {'baz': [
387
 
                {'message_id': 'foo', 'headers': {'From': 'me', 'To': 'you'}}
388
 
            ]}):
 
403
        archive = {
 
404
            'baz': [
 
405
                make_message('foo', headers={'From': 'me', 'To': 'you'})]}
 
406
        with ForkedFakeService.from_client(client, archive):
389
407
            response = client.get_messages('baz', headers=[
390
408
                'From', 'To'])
391
409
        first_message = response['messages'][0]
394
412
 
395
413
    def test_get_messages_max_body_length(self):
396
414
        client = GrackleClient('localhost', 8443)
397
 
        with ForkedFakeService.from_client(client,
398
 
            {'baz': [
399
 
                {'message_id': 'foo', 'body': u'abcdefghi'}
400
 
            ]}):
 
415
        archive = {'baz': [make_message('foo', body=u'abcdefghi')]}
 
416
        with ForkedFakeService.from_client(client, archive):
401
417
            response = client.get_messages('baz', max_body_length=3)
402
418
        first_message = response['messages'][0]
403
419
        self.assertEqual('abc', first_message['body'])
404
420
 
405
421
    def test_include_hidden(self):
406
422
        client = GrackleClient('localhost', 8444)
407
 
        with ForkedFakeService.from_client(client,
408
 
            {'baz': [
409
 
                {'message_id': 'foo', 'hidden': True},
410
 
                {'message_id': 'bar', 'hidden': False}
411
 
            ]}):
 
423
        archive = {
 
424
            'baz': [
 
425
                make_message('foo', hidden=True),
 
426
                make_message('bar', hidden=False),
 
427
            ]}
 
428
        with ForkedFakeService.from_client(client, archive):
412
429
            response = client.get_messages('baz', include_hidden=True)
413
430
            self.assertMessageIDs(['bar', 'foo'], response['messages'])
414
431
            response = client.get_messages('baz', include_hidden=False)
415
432
            self.assertMessageIDs(['bar'], response['messages'])
 
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'])