18
18
from testtools import ExpectedException
20
from grackle.client import (
20
from grackle.client import GrackleClient
21
from grackle.error import (
22
23
UnparsableDateRange,
23
24
UnsupportedDisplayType,
27
from grackle.service import ForkedFakeService
26
28
from grackle.store import (
32
34
def make_message(message_id, body='body', headers=None, hidden=False):
33
35
if headers is None:
35
headers['Message-Id'] = message_id
37
'message_id': message_id,
39
'thread_id': message_id,
40
'date': headers.get('date', '2005-01-01'),
41
'subject': headers.get('subject', 'subject'),
42
'author': headers.get('author', 'author'),
45
'replies': headers.get('in-reply-to', None),
38
'Message-Id': message_id,
44
message_headers.update(headers.items())
46
message.set_payload(body)
47
for key, value in message_headers.items():
49
return make_json_message(message_id, message.as_string(), hidden)
51
52
def make_mime_message(message_id, body='body', headers=None, hidden=False,
52
53
attachment_type=None):
55
54
parts = MIMEMultipart()
56
55
parts.attach(MIMEText(body))
57
56
if attachment_type is not None:
60
59
attachment['Content-Type'] = attachment_type
61
60
attachment['Content-Disposition'] = 'attachment; filename="file.ext"'
62
61
parts.attach(attachment)
64
message.set_payload(parts.as_string())
65
for key, value in headers.items():
67
return make_json_message(message_id, message.as_string())
70
class ForkedFakeService:
62
return make_message(message_id, parts.as_string(), headers, hidden)
65
class XXXForkedFakeService:
71
66
"""A Grackle service fake, as a ContextManager."""
73
68
def __init__(self, port, message_archives=None, write_logs=False):
119
114
"""Start the HTTP server."""
120
115
service = HTTPServer(('', self.port), FakeGrackleRequestHandler)
121
116
service.store = MemoryStore(self.message_archives)
122
for archive_id, messages in service.store.message_archives.iteritems():
123
for message in messages:
124
message.setdefault('headers', {})
126
118
if self.write_logs:
127
119
logging.basicConfig(
140
132
self.logger = logging.getLogger('http')
141
133
BaseHTTPRequestHandler.__init__(self, *args, **kwargs)
136
"""Create an archive or message on PUT."""
137
scheme, netloc, path, params, query_string, fragments = (
139
parts = path.split('/')
140
if parts[1] != 'archive':
141
# This is an unknonwn operation?
144
# This expected path is /archive/archive_id.
146
self.server.store.put_archive(parts[2])
147
self.send_response(httplib.CREATED)
150
except Exception, error:
152
httplib.BAD_REQUEST, error.__doc__)
154
# This expected path is /archive/archive_id/message_id.
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)
162
self.send_error(httplib.BAD_REQUEST)
143
164
def do_POST(self):
144
"""Create a message on POST."""
145
message = self.rfile.read(int(self.headers['content-length']))
165
"""Change a message on POST."""
146
166
scheme, netloc, path, params, query_string, fragments = (
147
167
urlparse(self.path))
148
168
parts = path.split('/')
149
if parts[1] == 'archive' and len(parts) == 4:
169
if parts[1] != 'archive':
170
# This is an unknonwn operation?
173
# This expected path is /archive/archive_id/message_id.
151
175
# This expected path is /archive/archive_id/message_id.
152
self.server.store.put_message(parts[2], parts[3], message)
153
self.send_response(httplib.CREATED)
176
response = self.server.store.hide_message(
177
parts[2], parts[3], query_string)
178
self.send_response(httplib.OK)
154
179
self.end_headers()
180
self.wfile.write(simplejson.dumps(response))
157
182
self.send_error(httplib.BAD_REQUEST)
180
205
self.logger.info(message)
208
class TestPutArchive(TestCase):
210
def test_put_archive(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']))
218
def test_put_archive_existing_archive(self):
219
client = GrackleClient('localhost', 8411)
220
message_archives = {'arch1': []}
221
with ForkedFakeService.from_client(client, message_archives):
222
with ExpectedException(ArchiveIdExists, ''):
223
client.put_archive('arch1')
183
226
class TestPutMessage(TestCase):
185
228
def test_put_message(self):
245
288
def get_messages_member_order_test(self, key):
246
289
client = GrackleClient('localhost', 8439)
249
make_message('foo', headers={key: '2011-03-25'}),
250
make_message('bar', headers={key: '2011-03-24'}),
296
make_message('foo', headers={header_name: '2011-03-25'}),
297
make_message('bar', headers={header_name: '2011-03-24'}),
252
299
with ForkedFakeService.from_client(client, archive):
253
300
response = client.get_messages('baz')
400
447
first_message = response['messages'][0]
401
448
self.assertEqual('foo', first_message['message_id'])
402
449
self.assertEqual(
403
{'From': 'me', 'Message-Id': 'foo', 'To': 'you'},
404
first_message['headers'])
450
archive['baz'][0]['headers'], first_message['headers'])
405
451
self.assertNotIn('body', first_message)
407
453
def test_display_type_text_only(self):
477
523
with ForkedFakeService.from_client(client, archive):
478
524
with ExpectedException(UnparsableDateRange, ''):
479
525
client.get_messages('baz', date_range='2012-01..12-02..12-03')
528
class TestHideMessages(TestCase):
530
def test_hide_message_true(self):
531
client = GrackleClient('localhost', 8470)
534
make_message('foo', hidden=False),
536
with ForkedFakeService.from_client(client, archive):
537
response = client.hide_message('baz', 'foo', hidden=True)
538
self.assertEqual('foo', response['message_id'])
539
self.assertIs(True, response['hidden'])
541
def test_hide_message_false(self):
542
client = GrackleClient('localhost', 8470)
545
make_message('foo', hidden=True),
547
with ForkedFakeService.from_client(client, archive):
548
response = client.hide_message('baz', 'foo', hidden=False)
549
self.assertEqual('foo', response['message_id'])
550
self.assertIs(False, response['hidden'])