31
33
def make_message(message_id, body='body', headers=None, hidden=False):
32
34
if headers is None:
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),
37
'Message-Id': message_id,
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)
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)
63
64
class ForkedFakeService:
64
65
"""A Grackle service fake, as a ContextManager."""
66
def __init__(self, port, messages=None, write_logs=False):
67
def __init__(self, port, message_archives=None, write_logs=False):
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.
78
if message_archives is None:
79
self.message_archives = {}
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
85
def from_client(client, messages=None):
86
def from_client(client, message_archives=None):
86
87
"""Instantiate a ForkedFakeService from the client.
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.
93
return ForkedFakeService(client.port, messages)
94
return ForkedFakeService(client.port, message_archives)
95
96
def is_ready(self):
96
97
"""Tell the parent process that the server is ready for writes."""
133
134
self.logger = logging.getLogger('http')
134
135
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)
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)
144
self.send_error(httplib.BAD_REQUEST)
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)
146
186
def do_GET(self):
147
187
"""Retrieve a list of messages on GET."""
167
207
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')
170
228
class TestPutMessage(TestCase):
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'])
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'))
181
248
class TestGetMessages(TestCase):
223
290
def get_messages_member_order_test(self, key):
224
291
client = GrackleClient('localhost', 8439)
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'}),
230
301
with ForkedFakeService.from_client(client, archive):
231
302
response = client.get_messages('baz')
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')
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'])