~didrocks/unity/altf10

« back to all changes in this revision

Viewing changes to grackle/tests/test_client.py

  • Committer: Curtis Hovey
  • Date: 2012-03-17 21:05:53 UTC
  • Revision ID: curtis.hovey@canonical.com-20120317210553-03bcgt1j7v9ntvzi
Always return the error reason.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
from BaseHTTPServer import (
2
 
    HTTPServer,
3
 
    BaseHTTPRequestHandler,
4
 
    )
5
1
from email.message import Message
6
2
from email.mime.multipart import MIMEMultipart
7
3
from email.mime.text import MIMEText
8
 
import httplib
9
 
import logging
10
 
import os
11
 
from signal import SIGKILL
12
 
import simplejson
13
4
from StringIO import StringIO
14
 
import sys
15
5
from unittest import TestCase
16
 
from urlparse import urlparse
17
6
 
18
7
from testtools import ExpectedException
19
8
 
20
 
from grackle.client import (
21
 
    GrackleClient,
 
9
from grackle.client import GrackleClient
 
10
from grackle.error import (
 
11
    ArchiveIdExists,
22
12
    UnparsableDateRange,
23
13
    UnsupportedDisplayType,
24
14
    UnsupportedOrder,
25
15
    )
 
16
from grackle.service import ForkedFakeService
26
17
from grackle.store import (
27
 
    MemoryStore,
 
18
    make_json_message,
28
19
    )
29
20
 
30
21
 
31
22
def make_message(message_id, body='body', headers=None, hidden=False):
32
23
    if headers is None:
33
24
        headers = {}
34
 
    headers['Message-Id'] = message_id
35
 
    message = {
36
 
        'message_id': message_id,
37
 
        'headers': headers,
38
 
        'thread_id': message_id,
39
 
        'date': headers.get('date', '2005-01-01'),
40
 
        'subject': headers.get('subject', 'subject'),
41
 
        'author': headers.get('author', 'author'),
42
 
        'hidden': hidden,
43
 
        'attachments': [],
44
 
        'replies': headers.get('in-reply-to', None),
45
 
        'body': body,
 
25
    message_headers = {
 
26
        'Message-Id': message_id,
 
27
        'date': '2005-01-01',
 
28
        'subject': 'subject',
 
29
        'from': 'author',
 
30
        'replies': '',
46
31
        }
47
 
    return message
 
32
    message_headers.update(headers.items())
 
33
    message = Message()
 
34
    message.set_payload(body)
 
35
    for key, value in message_headers.items():
 
36
        message[key] = value
 
37
    return make_json_message(message_id, message.as_string(), hidden)
48
38
 
49
39
 
50
40
def make_mime_message(message_id, body='body', headers=None, hidden=False,
51
41
                      attachment_type=None):
52
 
    message = MIMEMultipart()
53
 
    message.attach(MIMEText(body))
 
42
    parts = MIMEMultipart()
 
43
    parts.attach(MIMEText(body))
54
44
    if attachment_type is not None:
55
45
        attachment = Message()
56
46
        attachment.set_payload('attactment data.')
57
47
        attachment['Content-Type'] = attachment_type
58
48
        attachment['Content-Disposition'] = 'attachment; filename="file.ext"'
59
 
        message.attach(attachment)
60
 
    return make_message(message_id, message.get_payload(), headers, hidden)
61
 
 
62
 
 
63
 
class ForkedFakeService:
64
 
    """A Grackle service fake, as a ContextManager."""
65
 
 
66
 
    def __init__(self, port, messages=None, write_logs=False):
67
 
        """Constructor.
68
 
 
69
 
        :param port: The tcp port to use.
70
 
        :param messages: A dict of lists of dicts representing messages.  The
71
 
            outer dict represents the archive, the list represents the list of
72
 
            messages for that archive.
73
 
        :param write_logs: If true, log messages will be written to stdout.
74
 
        """
75
 
        self.pid = None
76
 
        self.port = port
77
 
        if messages is None:
78
 
            self.messages = {}
79
 
        else:
80
 
            self.messages = messages
81
 
        self.read_end, self.write_end = os.pipe()
82
 
        self.write_logs = write_logs
83
 
 
84
 
    @staticmethod
85
 
    def from_client(client, messages=None):
86
 
        """Instantiate a ForkedFakeService from the client.
87
 
 
88
 
        :param port: The client to provide service for.
89
 
        :param messages: A dict of lists of dicts representing messages.  The
90
 
            outer dict represents the archive, the list represents the list of
91
 
            messages for that archive.
92
 
        """
93
 
        return ForkedFakeService(client.port, messages)
94
 
 
95
 
    def is_ready(self):
96
 
        """Tell the parent process that the server is ready for writes."""
97
 
        os.write(self.write_end, 'asdf')
98
 
 
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."""
104
 
        pid = os.fork()
105
 
        if pid == 0:
106
 
            self.start_server()
107
 
        self.pid = pid
108
 
        os.read(self.read_end, 1)
109
 
        return
110
 
 
111
 
    def start_server(self):
112
 
        """Start the HTTP server."""
113
 
        service = HTTPServer(('', self.port), FakeGrackleRequestHandler)
114
 
        service.store = MemoryStore(self.messages)
115
 
        for archive_id, messages in service.store.messages.iteritems():
116
 
            for message in messages:
117
 
                message.setdefault('headers', {})
118
 
        self.is_ready()
119
 
        if self.write_logs:
120
 
            logging.basicConfig(
121
 
                stream=sys.stderr, level=logging.INFO)
122
 
        service.serve_forever()
123
 
 
124
 
    def __exit__(self, exc_type, exc_val, traceback):
125
 
        os.kill(self.pid, SIGKILL)
126
 
 
127
 
 
128
 
class FakeGrackleRequestHandler(BaseHTTPRequestHandler):
129
 
    """A request handler that forwards to server.store."""
130
 
 
131
 
    def __init__(self, *args, **kwargs):
132
 
        """Constructor.  Sets up logging."""
133
 
        self.logger = logging.getLogger('http')
134
 
        BaseHTTPRequestHandler.__init__(self, *args, **kwargs)
135
 
 
136
 
    def do_POST(self):
137
 
        """Create a message on POST."""
138
 
        message = self.rfile.read(int(self.headers['content-length']))
139
 
        if message == 'This is a message':
140
 
            self.send_response(httplib.CREATED)
141
 
            self.end_headers()
142
 
            self.wfile.close()
143
 
        else:
144
 
            self.send_error(httplib.BAD_REQUEST)
145
 
 
146
 
    def do_GET(self):
147
 
        """Retrieve a list of messages on GET."""
148
 
        scheme, netloc, path, params, query_string, fragments = (
149
 
            urlparse(self.path))
150
 
        parts = path.split('/')
151
 
        if parts[1] == 'archive':
152
 
            try:
153
 
                response = self.server.store.get_messages(
154
 
                    parts[2], query_string)
155
 
                self.send_response(httplib.OK)
156
 
                self.end_headers()
157
 
                self.wfile.write(simplejson.dumps(response))
158
 
            except Exception, error:
159
 
                self.send_response(
160
 
                    httplib.BAD_REQUEST, error.__doc__)
161
 
                return
162
 
 
163
 
    def log_message(self, format, *args):
164
 
        """Override log_message to use standard Python logging."""
165
 
        message = "%s - - [%s] %s\n" % (
166
 
            self.address_string(), self.log_date_time_string(), format % args)
167
 
        self.logger.info(message)
 
49
        parts.attach(attachment)
 
50
    return make_message(message_id, parts.as_string(), headers, hidden)
 
51
 
 
52
 
 
53
class TestPutArchive(TestCase):
 
54
 
 
55
    def test_put_archive(self):
 
56
        client = GrackleClient('localhost', 8410)
 
57
        message_archives = {}
 
58
        with ForkedFakeService.from_client(client, message_archives):
 
59
            client.put_archive('arch1')
 
60
            response = client.get_messages('arch1')
 
61
        self.assertEqual(0, len(response['messages']))
 
62
 
 
63
    def test_put_archive_existing_archive(self):
 
64
        client = GrackleClient('localhost', 8411)
 
65
        message_archives = {'arch1': []}
 
66
        with ForkedFakeService.from_client(client, message_archives):
 
67
            with ExpectedException(ArchiveIdExists, ''):
 
68
                client.put_archive('arch1')
168
69
 
169
70
 
170
71
class TestPutMessage(TestCase):
171
72
 
172
73
    def test_put_message(self):
173
74
        client = GrackleClient('localhost', 8420)
174
 
        with ForkedFakeService.from_client(client):
175
 
            client.put_message('arch1', 'asdf', StringIO('This is a message'))
 
75
        message_archives = {'arch1': []}
 
76
        with ForkedFakeService.from_client(client, message_archives):
 
77
            client.put_message('arch1', 'id1', StringIO('This is a message'))
 
78
            response = client.get_messages('arch1')
 
79
        self.assertEqual(1, len(response['messages']))
 
80
        message = response['messages'][0]
 
81
        self.assertEqual('id1', message['message_id'])
 
82
 
 
83
    def test_put_message_without_archive(self):
 
84
        client = GrackleClient('localhost', 8421)
 
85
        message_archives = {'arch1': []}
 
86
        with ForkedFakeService.from_client(client, message_archives):
176
87
            with ExpectedException(Exception, 'wtf'):
177
 
                client.put_message('arch1', 'asdf',
178
 
                    StringIO('This is not a message'))
 
88
                client.put_message('no-archive', 'id1', StringIO('message'))
179
89
 
180
90
 
181
91
class TestGetMessages(TestCase):
222
132
 
223
133
    def get_messages_member_order_test(self, key):
224
134
        client = GrackleClient('localhost', 8439)
 
135
        if key == 'author':
 
136
            header_name = 'from'
 
137
        else:
 
138
            header_name = key
225
139
        archive = {
226
140
            'baz': [
227
 
                make_message('foo', headers={key: '2011-03-25'}),
228
 
                make_message('bar', headers={key: '2011-03-24'}),
 
141
                make_message('foo', headers={header_name: '2011-03-25'}),
 
142
                make_message('bar', headers={header_name: '2011-03-24'}),
229
143
             ]}
230
144
        with ForkedFakeService.from_client(client, archive):
231
145
            response = client.get_messages('baz')
378
292
        first_message = response['messages'][0]
379
293
        self.assertEqual('foo', first_message['message_id'])
380
294
        self.assertEqual(
381
 
            {'From': 'me', 'Message-Id': 'foo', 'To': 'you'},
382
 
            first_message['headers'])
 
295
            archive['baz'][0]['headers'], first_message['headers'])
383
296
        self.assertNotIn('body', first_message)
384
297
 
385
298
    def test_display_type_text_only(self):
396
309
        self.assertEqual('foo', first_message['message_id'])
397
310
        self.assertEqual('me', first_message['headers']['From'])
398
311
        self.assertEqual('you', first_message['headers']['To'])
399
 
        self.assertEqual('abcdefghi', first_message['body'])
 
312
        self.assertEqual(archive['baz'][0]['body'], first_message['body'])
400
313
 
401
314
    def test_display_type_all(self):
402
315
        client = GrackleClient('localhost', 8447)
412
325
        self.assertEqual('foo', first_message['message_id'])
413
326
        self.assertEqual('me', first_message['headers']['From'])
414
327
        self.assertEqual('you', first_message['headers']['To'])
415
 
        self.assertEqual(
416
 
            'abcdefghi\n\nattactment data.', first_message['body'])
 
328
        self.assertEqual(archive['baz'][0]['body'], first_message['body'])
417
329
 
418
330
    def test_date_range(self):
419
331
        client = GrackleClient('localhost', 8448)
456
368
        with ForkedFakeService.from_client(client, archive):
457
369
            with ExpectedException(UnparsableDateRange, ''):
458
370
                client.get_messages('baz', date_range='2012-01..12-02..12-03')
 
371
 
 
372
 
 
373
class TestHideMessages(TestCase):
 
374
 
 
375
    def test_hide_message_true(self):
 
376
        client = GrackleClient('localhost', 8470)
 
377
        archive = {
 
378
            'baz': [
 
379
                make_message('foo', hidden=False),
 
380
            ]}
 
381
        with ForkedFakeService.from_client(client, archive):
 
382
            response = client.hide_message('baz', 'foo', hidden=True)
 
383
        self.assertEqual('foo', response['message_id'])
 
384
        self.assertIs(True, response['hidden'])
 
385
 
 
386
    def test_hide_message_false(self):
 
387
        client = GrackleClient('localhost', 8470)
 
388
        archive = {
 
389
            'baz': [
 
390
                make_message('foo', hidden=True),
 
391
            ]}
 
392
        with ForkedFakeService.from_client(client, archive):
 
393
            response = client.hide_message('baz', 'foo', hidden=False)
 
394
        self.assertEqual('foo', response['message_id'])
 
395
        self.assertIs(False, response['hidden'])