~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:16:12 UTC
  • Revision ID: curtis.hovey@canonical.com-20120316201612-lr7b32umqgduaja6
Added a rudimentary put_archive.

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
20
from grackle.client import (
19
21
    GrackleClient,
 
22
    UnparsableDateRange,
 
23
    UnsupportedDisplayType,
20
24
    UnsupportedOrder,
21
25
    )
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):
 
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
        """
117
75
        self.pid = None
118
76
        self.port = port
119
 
        if messages is None:
120
 
            self.messages = {}
 
77
        if message_archives is None:
 
78
            self.message_archives = {}
121
79
        else:
122
 
            self.messages = messages
 
80
            self.message_archives = message_archives
123
81
        self.read_end, self.write_end = os.pipe()
124
82
        self.write_logs = write_logs
125
83
 
126
84
    @staticmethod
127
 
    def from_client(client, messages=None):
128
 
        return ForkedFake(client.port, messages)
 
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)
129
94
 
130
95
    def is_ready(self):
 
96
        """Tell the parent process that the server is ready for writes."""
131
97
        os.write(self.write_end, 'asdf')
132
98
 
133
99
    def __enter__(self):
 
100
        """Run the service.
 
101
 
 
102
        Fork and start a server in the child.  Return when the server is ready
 
103
        for use."""
134
104
        pid = os.fork()
135
105
        if pid == 0:
136
106
            self.start_server()
139
109
        return
140
110
 
141
111
    def start_server(self):
 
112
        """Start the HTTP server."""
142
113
        service = HTTPServer(('', self.port), FakeGrackleRequestHandler)
143
 
        service.store = GrackleStore(self.messages)
144
 
        for archive_id, messages in service.store.messages.iteritems():
 
114
        service.store = MemoryStore(self.message_archives)
 
115
        for archive_id, messages in service.store.message_archives.iteritems():
145
116
            for message in messages:
146
117
                message.setdefault('headers', {})
147
118
        self.is_ready()
154
125
        os.kill(self.pid, SIGKILL)
155
126
 
156
127
 
157
 
SUPPORTED_ORDERS = set(
158
 
    ['date', 'author', 'subject', 'thread_newest', 'thread_oldest',
159
 
     'thread_subject'])
160
 
 
161
 
 
162
128
class FakeGrackleRequestHandler(BaseHTTPRequestHandler):
 
129
    """A request handler that forwards to server.store."""
163
130
 
164
131
    def __init__(self, *args, **kwargs):
 
132
        """Constructor.  Sets up logging."""
165
133
        self.logger = logging.getLogger('http')
166
134
        BaseHTTPRequestHandler.__init__(self, *args, **kwargs)
167
135
 
 
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
 
168
164
    def do_POST(self):
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)
 
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)
176
183
 
177
184
    def do_GET(self):
 
185
        """Retrieve a list of messages on GET."""
178
186
        scheme, netloc, path, params, query_string, fragments = (
179
187
            urlparse(self.path))
180
188
        parts = path.split('/')
185
193
                self.send_response(httplib.OK)
186
194
                self.end_headers()
187
195
                self.wfile.write(simplejson.dumps(response))
188
 
            except UnsupportedOrder:
189
 
                self.send_response(httplib.BAD_REQUEST)
190
 
                self.wfile.write('Unsupported order')
 
196
            except Exception, error:
 
197
                self.send_response(
 
198
                    httplib.BAD_REQUEST, error.__doc__)
191
199
                return
192
200
 
193
201
    def log_message(self, format, *args):
 
202
        """Override log_message to use standard Python logging."""
194
203
        message = "%s - - [%s] %s\n" % (
195
 
            self.address_string(), self.log_date_time_string(), format%args)
 
204
            self.address_string(), self.log_date_time_string(), format % args)
196
205
        self.logger.info(message)
197
206
 
198
207
 
 
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
 
199
219
class TestPutMessage(TestCase):
200
220
 
201
221
    def test_put_message(self):
202
 
        client = GrackleClient('localhost', 8436)
203
 
        with ForkedFake.from_client(client):
204
 
            client.put_message('arch1', 'asdf', StringIO('This is a message'))
 
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):
205
235
            with ExpectedException(Exception, 'wtf'):
206
 
                client.put_message('arch1', 'asdf',
207
 
                    StringIO('This is not a message'))
 
236
                client.put_message('no-archive', 'id1', StringIO('message'))
208
237
 
209
238
 
210
239
class TestGetMessages(TestCase):
214
243
 
215
244
    def assertMessageIDs(self, ids, messages):
216
245
        self.assertIDOrder(
217
 
            sorted(ids), sorted(messages, key=lambda m:m['message_id']))
 
246
            sorted(ids), sorted(messages, key=lambda m: m['message_id']))
218
247
 
219
248
    def test_get_messages(self):
220
 
        client = GrackleClient('localhost', 8435)
221
 
        with ForkedFake.from_client(client,
222
 
            {'baz':
223
 
            [{'message_id': 'foo'},
224
 
             {'message_id': 'bar'}]}):
 
249
        client = GrackleClient('localhost', 8430)
 
250
        archive = {
 
251
            'baz': [make_message('foo'), make_message('bar')]}
 
252
        with ForkedFakeService.from_client(client, archive):
225
253
            response = client.get_messages('baz')
226
254
        self.assertEqual(['bar', 'foo'], sorted(m['message_id'] for m in
227
255
            response['messages']))
230
258
 
231
259
    def test_get_messages_by_id(self):
232
260
        client = GrackleClient('localhost', 8437)
233
 
        with ForkedFake.from_client(client,
234
 
            {'baz':
235
 
            [{'message_id': 'foo'},
236
 
             {'message_id': 'bar'}]}):
 
261
        archive = {
 
262
            'baz': [make_message('foo'), make_message('bar')]}
 
263
        with ForkedFakeService.from_client(client, archive):
237
264
            response = client.get_messages('baz', message_ids=['foo'])
238
265
        message, = response['messages']
239
266
        self.assertEqual('foo', message['message_id'])
240
267
 
241
268
    def test_get_messages_batching(self):
242
269
        client = GrackleClient('localhost', 8438)
243
 
        with ForkedFake.from_client(client,
244
 
            {'baz':
245
 
            [{'message_id': 'foo'},
246
 
             {'message_id': 'bar'}]}):
 
270
        archive = {'baz': [make_message('foo'), make_message('bar')]}
 
271
        with ForkedFakeService.from_client(client, archive):
247
272
            response = client.get_messages('baz', limit=1)
248
273
            self.assertEqual(1, len(response['messages']))
249
274
            messages = response['messages']
255
280
 
256
281
    def get_messages_member_order_test(self, key):
257
282
        client = GrackleClient('localhost', 8439)
258
 
        with ForkedFake.from_client(client,
259
 
                {'baz': [{'message_id': 'foo', key: '2011-03-25'},
260
 
                 {'message_id': 'bar', key: '2011-03-24'}]}):
 
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):
261
293
            response = client.get_messages('baz')
262
294
            self.assertIDOrder(['foo', 'bar'], response['messages'])
263
295
            response = client.get_messages('baz', order=key)
273
305
        self.get_messages_member_order_test('subject')
274
306
 
275
307
    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
             ]}
276
315
        client = GrackleClient('localhost', 8439)
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
 
            ]}):
 
316
        with ForkedFakeService.from_client(client, archive):
282
317
            response = client.get_messages('baz')
283
318
            self.assertIDOrder(['bar', 'qux', 'foo'], response['messages'])
284
319
            response = client.get_messages('baz', order='subject')
288
323
 
289
324
    def test_get_messages_thread_oldest_order(self):
290
325
        client = GrackleClient('localhost', 8439)
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
 
            ]}):
 
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):
296
334
            response = client.get_messages('baz')
297
335
            self.assertIDOrder(['bar', 'qux', 'foo'], response['messages'])
298
336
            response = client.get_messages('baz', order='date')
302
340
 
303
341
    def test_get_messages_thread_newest_order(self):
304
342
        client = GrackleClient('localhost', 8439)
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
 
            ]}):
 
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):
311
353
            response = client.get_messages('baz', order='date')
312
354
            self.assertIDOrder(
313
355
                ['qux', 'bar', 'foo', 'baz'], response['messages'])
317
359
 
318
360
    def test_get_messages_unsupported_order(self):
319
361
        client = GrackleClient('localhost', 8439)
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):
 
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, ''):
324
369
                client.get_messages('baz', order='nonsense')
325
370
 
326
371
    def test_get_messages_headers_no_headers(self):
327
372
        client = GrackleClient('localhost', 8440)
328
 
        with ForkedFake.from_client(client,
329
 
            {'baz': [
330
 
                {'message_id': 'foo'}
331
 
            ]}):
 
373
        archive = {'baz': [make_message('foo')]}
 
374
        with ForkedFakeService.from_client(client, archive):
332
375
            response = client.get_messages('baz', headers=[
333
376
                'Subject', 'Date', 'X-Launchpad-Message-Rationale'])
334
377
        first_message = response['messages'][0]
337
380
 
338
381
    def test_get_messages_headers_exclude_headers(self):
339
382
        client = GrackleClient('localhost', 8441)
340
 
        with ForkedFake.from_client(client,
341
 
            {'baz': [
342
 
                {'message_id': 'foo', 'headers': {'From': 'me'}}
343
 
            ]}):
 
383
        archive = {
 
384
            'baz': [make_message('foo', headers={'From': 'me'})]}
 
385
        with ForkedFakeService.from_client(client, archive):
344
386
            response = client.get_messages('baz', headers=[
345
387
                'Subject', 'Date', 'X-Launchpad-Message-Rationale'])
346
388
        first_message = response['messages'][0]
349
391
 
350
392
    def test_get_messages_headers_include_headers(self):
351
393
        client = GrackleClient('localhost', 8442)
352
 
        with ForkedFake.from_client(client,
353
 
            {'baz': [
354
 
                {'message_id': 'foo', 'headers': {'From': 'me', 'To': 'you'}}
355
 
            ]}):
 
394
        archive = {
 
395
            'baz': [
 
396
                make_message('foo', headers={'From': 'me', 'To': 'you'})]}
 
397
        with ForkedFakeService.from_client(client, archive):
356
398
            response = client.get_messages('baz', headers=[
357
399
                'From', 'To'])
358
400
        first_message = response['messages'][0]
361
403
 
362
404
    def test_get_messages_max_body_length(self):
363
405
        client = GrackleClient('localhost', 8443)
364
 
        with ForkedFake.from_client(client,
365
 
            {'baz': [
366
 
                {'message_id': 'foo', 'body': u'abcdefghi'}
367
 
            ]}):
 
406
        archive = {'baz': [make_message('foo', body=u'abcdefghi')]}
 
407
        with ForkedFakeService.from_client(client, archive):
368
408
            response = client.get_messages('baz', max_body_length=3)
369
409
        first_message = response['messages'][0]
370
410
        self.assertEqual('abc', first_message['body'])
371
411
 
372
412
    def test_include_hidden(self):
373
413
        client = GrackleClient('localhost', 8444)
374
 
        with ForkedFake.from_client(client,
375
 
            {'baz': [
376
 
                {'message_id': 'foo', 'hidden': True},
377
 
                {'message_id': 'bar', 'hidden': False}
378
 
            ]}):
 
414
        archive = {
 
415
            'baz': [
 
416
                make_message('foo', hidden=True),
 
417
                make_message('bar', hidden=False),
 
418
            ]}
 
419
        with ForkedFakeService.from_client(client, archive):
379
420
            response = client.get_messages('baz', include_hidden=True)
380
421
            self.assertMessageIDs(['bar', 'foo'], response['messages'])
381
422
            response = client.get_messages('baz', include_hidden=False)
382
423
            self.assertMessageIDs(['bar'], response['messages'])
383
424
 
 
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'])