~didrocks/unity/altf10

« back to all changes in this revision

Viewing changes to grackle/tests/test_client.py

  • Committer: Aaron Bentley
  • Date: 2012-01-16 16:42:59 UTC
  • Revision ID: aaron@canonical.com-20120116164259-qcx32bgzhabmopnc
Cleaner logging switch.

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