33
31
def make_message(message_id, body='body', headers=None, hidden=False):
34
32
if headers is None:
37
'Message-Id': message_id,
34
headers['Message-Id'] = message_id
36
'message_id': message_id,
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'),
44
'replies': headers.get('in-reply-to', None),
43
message_headers.update(headers.items())
45
message.set_payload(body)
46
for key, value in message_headers.items():
48
return make_json_message(message_id, message.as_string(), hidden)
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)
64
63
class ForkedFakeService:
65
64
"""A Grackle service fake, as a ContextManager."""
67
def __init__(self, port, message_archives=None, write_logs=False):
66
def __init__(self, port, messages=None, write_logs=False):
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.
78
if message_archives is None:
79
self.message_archives = {}
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
86
def from_client(client, message_archives=None):
85
def from_client(client, messages=None):
87
86
"""Instantiate a ForkedFakeService from the client.
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.
94
return ForkedFakeService(client.port, message_archives)
93
return ForkedFakeService(client.port, messages)
96
95
def is_ready(self):
97
96
"""Tell the parent process that the server is ready for writes."""
134
133
self.logger = logging.getLogger('http')
135
134
BaseHTTPRequestHandler.__init__(self, *args, **kwargs)
138
"""Create an archive or message on PUT."""
139
scheme, netloc, path, params, query_string, fragments = (
141
parts = path.split('/')
142
if parts[1] != 'archive':
143
# This is an unknonwn operation?
146
# This expected path is /archive/archive_id.
148
self.server.store.put_archive(parts[2])
149
self.send_response(httplib.CREATED)
152
except Exception, error:
154
httplib.BAD_REQUEST, error.__doc__)
156
# This expected path is /archive/archive_id/message_id.
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)
164
self.send_error(httplib.BAD_REQUEST)
166
136
def do_POST(self):
167
"""Change a message on POST."""
168
scheme, netloc, path, params, query_string, fragments = (
170
parts = path.split('/')
171
if parts[1] != 'archive':
172
# This is an unknonwn operation?
175
# This expected path is /archive/archive_id/message_id.
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)
182
self.wfile.write(simplejson.dumps(response))
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)
144
self.send_error(httplib.BAD_REQUEST)
186
146
def do_GET(self):
187
147
"""Retrieve a list of messages on GET."""
207
167
self.logger.info(message)
210
class TestPutArchive(TestCase):
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']))
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')
228
170
class TestPutMessage(TestCase):
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'])
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'))
248
181
class TestGetMessages(TestCase):
290
223
def get_messages_member_order_test(self, key):
291
224
client = GrackleClient('localhost', 8439)
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'}),
301
230
with ForkedFakeService.from_client(client, archive):
302
231
response = client.get_messages('baz')
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')
530
class TestHideMessages(TestCase):
532
def test_hide_message_true(self):
533
client = GrackleClient('localhost', 8470)
536
make_message('foo', hidden=False),
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'])
543
def test_hide_message_false(self):
544
client = GrackleClient('localhost', 8470)
547
make_message('foo', hidden=True),
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'])