~didrocks/unity/altf10

« back to all changes in this revision

Viewing changes to grackle/tests/test_client.py

  • Committer: Curtis Hovey
  • Date: 2012-02-24 21:31:01 UTC
  • Revision ID: curtis.hovey@canonical.com-20120224213101-jac3b2aj4nd5uwpj
Separate gracle MemoryStore to its own module.

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
 
18
18
from testtools import ExpectedException
19
19
 
20
 
from grackle.client import GrackleClient
21
 
from grackle.error import (
22
 
    ArchiveIdExists,
 
20
from grackle.client import (
 
21
    GrackleClient,
23
22
    UnparsableDateRange,
24
23
    UnsupportedDisplayType,
25
24
    UnsupportedOrder,
26
25
    )
27
26
from grackle.store import (
28
 
    make_json_message,
29
27
    MemoryStore,
30
28
    )
31
29
 
33
31
def make_message(message_id, body='body', headers=None, hidden=False):
34
32
    if headers is None:
35
33
        headers = {}
36
 
    message_headers = {
37
 
        'Message-Id': message_id,
38
 
        'date': '2005-01-01',
39
 
        'subject': 'subject',
40
 
        'from': 'author',
41
 
        'replies': '',
 
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,
42
46
        }
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)
 
47
    return message
49
48
 
50
49
 
51
50
def make_mime_message(message_id, body='body', headers=None, hidden=False,
52
51
                      attachment_type=None):
53
 
    parts = MIMEMultipart()
54
 
    parts.attach(MIMEText(body))
 
52
    message = MIMEMultipart()
 
53
    message.attach(MIMEText(body))
55
54
    if attachment_type is not None:
56
55
        attachment = Message()
57
56
        attachment.set_payload('attactment data.')
58
57
        attachment['Content-Type'] = attachment_type
59
58
        attachment['Content-Disposition'] = 'attachment; filename="file.ext"'
60
 
        parts.attach(attachment)
61
 
    return make_message(message_id, parts.as_string(), headers, hidden)
 
59
        message.attach(attachment)
 
60
    return make_message(message_id, message.get_payload(), headers, hidden)
62
61
 
63
62
 
64
63
class ForkedFakeService:
65
64
    """A Grackle service fake, as a ContextManager."""
66
65
 
67
 
    def __init__(self, port, message_archives=None, write_logs=False):
 
66
    def __init__(self, port, messages=None, write_logs=False):
68
67
        """Constructor.
69
68
 
70
69
        :param port: The tcp port to use.
71
 
        :param message_archives: A dict of lists of dicts representing
72
 
            archives of messages. The outer dict represents the archive,
73
 
            the list represents the list of messages for that archive.
 
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.
74
73
        :param write_logs: If true, log messages will be written to stdout.
75
74
        """
76
75
        self.pid = None
77
76
        self.port = port
78
 
        if message_archives is None:
79
 
            self.message_archives = {}
 
77
        if messages is None:
 
78
            self.messages = {}
80
79
        else:
81
 
            self.message_archives = message_archives
 
80
            self.messages = messages
82
81
        self.read_end, self.write_end = os.pipe()
83
82
        self.write_logs = write_logs
84
83
 
85
84
    @staticmethod
86
 
    def from_client(client, message_archives=None):
 
85
    def from_client(client, messages=None):
87
86
        """Instantiate a ForkedFakeService from the client.
88
87
 
89
88
        :param port: The client to provide service for.
90
 
        :param message_archives: A dict of lists of dicts representing
91
 
            archives of messages. The outer dict represents the archive,
92
 
            the list represents the list of messages for that archive.
 
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.
93
92
        """
94
 
        return ForkedFakeService(client.port, message_archives)
 
93
        return ForkedFakeService(client.port, messages)
95
94
 
96
95
    def is_ready(self):
97
96
        """Tell the parent process that the server is ready for writes."""
112
111
    def start_server(self):
113
112
        """Start the HTTP server."""
114
113
        service = HTTPServer(('', self.port), FakeGrackleRequestHandler)
115
 
        service.store = MemoryStore(self.message_archives)
116
 
        for archive_id, messages in service.store.message_archives.iteritems():
 
114
        service.store = MemoryStore(self.messages)
 
115
        for archive_id, messages in service.store.messages.iteritems():
117
116
            for message in messages:
118
117
                message.setdefault('headers', {})
119
118
        self.is_ready()
134
133
        self.logger = logging.getLogger('http')
135
134
        BaseHTTPRequestHandler.__init__(self, *args, **kwargs)
136
135
 
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
 
 
166
136
    def do_POST(self):
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)
 
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)
185
145
 
186
146
    def do_GET(self):
187
147
        """Retrieve a list of messages on GET."""
207
167
        self.logger.info(message)
208
168
 
209
169
 
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
 
 
228
170
class TestPutMessage(TestCase):
229
171
 
230
172
    def test_put_message(self):
231
173
        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):
 
174
        with ForkedFakeService.from_client(client):
 
175
            client.put_message('arch1', 'asdf', StringIO('This is a message'))
244
176
            with ExpectedException(Exception, 'wtf'):
245
 
                client.put_message('no-archive', 'id1', StringIO('message'))
 
177
                client.put_message('arch1', 'asdf',
 
178
                    StringIO('This is not a message'))
246
179
 
247
180
 
248
181
class TestGetMessages(TestCase):
289
222
 
290
223
    def get_messages_member_order_test(self, key):
291
224
        client = GrackleClient('localhost', 8439)
292
 
        if key == 'author':
293
 
            header_name = 'from'
294
 
        else:
295
 
            header_name = key
296
225
        archive = {
297
226
            'baz': [
298
 
                make_message('foo', headers={header_name: '2011-03-25'}),
299
 
                make_message('bar', headers={header_name: '2011-03-24'}),
 
227
                make_message('foo', headers={key: '2011-03-25'}),
 
228
                make_message('bar', headers={key: '2011-03-24'}),
300
229
             ]}
301
230
        with ForkedFakeService.from_client(client, archive):
302
231
            response = client.get_messages('baz')
449
378
        first_message = response['messages'][0]
450
379
        self.assertEqual('foo', first_message['message_id'])
451
380
        self.assertEqual(
452
 
            archive['baz'][0]['headers'], first_message['headers'])
 
381
            {'From': 'me', 'Message-Id': 'foo', 'To': 'you'},
 
382
            first_message['headers'])
453
383
        self.assertNotIn('body', first_message)
454
384
 
455
385
    def test_display_type_text_only(self):
466
396
        self.assertEqual('foo', first_message['message_id'])
467
397
        self.assertEqual('me', first_message['headers']['From'])
468
398
        self.assertEqual('you', first_message['headers']['To'])
469
 
        self.assertEqual(archive['baz'][0]['body'], first_message['body'])
 
399
        self.assertEqual('abcdefghi', first_message['body'])
470
400
 
471
401
    def test_display_type_all(self):
472
402
        client = GrackleClient('localhost', 8447)
482
412
        self.assertEqual('foo', first_message['message_id'])
483
413
        self.assertEqual('me', first_message['headers']['From'])
484
414
        self.assertEqual('you', first_message['headers']['To'])
485
 
        self.assertEqual(archive['baz'][0]['body'], first_message['body'])
 
415
        self.assertEqual(
 
416
            'abcdefghi\n\nattactment data.', first_message['body'])
486
417
 
487
418
    def test_date_range(self):
488
419
        client = GrackleClient('localhost', 8448)
525
456
        with ForkedFakeService.from_client(client, archive):
526
457
            with ExpectedException(UnparsableDateRange, ''):
527
458
                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'])