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