61
50
return make_message(message_id, parts.as_string(), headers, hidden)
64
class ForkedFakeService:
65
"""A Grackle service fake, as a ContextManager."""
67
def __init__(self, port, message_archives=None, write_logs=False):
70
: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.
74
: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
82
self.read_end, self.write_end = os.pipe()
83
self.write_logs = write_logs
86
def from_client(client, message_archives=None):
87
"""Instantiate a ForkedFakeService from the client.
89
: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.
94
return ForkedFakeService(client.port, message_archives)
97
"""Tell the parent process that the server is ready for writes."""
98
os.write(self.write_end, 'asdf')
103
Fork and start a server in the child. Return when the server is ready
109
os.read(self.read_end, 1)
112
def start_server(self):
113
"""Start the HTTP server."""
114
service = HTTPServer(('', self.port), FakeGrackleRequestHandler)
115
service.store = MemoryStore(self.message_archives)
119
stream=sys.stderr, level=logging.INFO)
120
service.serve_forever()
122
def __exit__(self, exc_type, exc_val, traceback):
123
os.kill(self.pid, SIGKILL)
126
class FakeGrackleRequestHandler(BaseHTTPRequestHandler):
127
"""A request handler that forwards to server.store."""
129
def __init__(self, *args, **kwargs):
130
"""Constructor. Sets up logging."""
131
self.logger = logging.getLogger('http')
132
BaseHTTPRequestHandler.__init__(self, *args, **kwargs)
135
"""Create an archive or message on PUT."""
136
scheme, netloc, path, params, query_string, fragments = (
138
parts = path.split('/')
139
if parts[1] != 'archive':
140
# This is an unknonwn operation?
143
# This expected path is /archive/archive_id.
145
self.server.store.put_archive(parts[2])
146
self.send_response(httplib.CREATED)
149
except Exception, error:
151
httplib.BAD_REQUEST, error.__doc__)
153
# This expected path is /archive/archive_id/message_id.
155
message = self.rfile.read(int(self.headers['content-length']))
156
self.server.store.put_message(parts[2], parts[3], message)
157
self.send_response(httplib.CREATED)
161
self.send_error(httplib.BAD_REQUEST)
164
"""Change a message on POST."""
165
scheme, netloc, path, params, query_string, fragments = (
167
parts = path.split('/')
168
if parts[1] != 'archive':
169
# This is an unknonwn operation?
172
# This expected path is /archive/archive_id/message_id.
174
# This expected path is /archive/archive_id/message_id.
175
response = self.server.store.hide_message(
176
parts[2], parts[3], query_string)
177
self.send_response(httplib.OK)
179
self.wfile.write(simplejson.dumps(response))
181
self.send_error(httplib.BAD_REQUEST)
184
"""Retrieve a list of messages on GET."""
185
scheme, netloc, path, params, query_string, fragments = (
187
parts = path.split('/')
188
if parts[1] == 'archive':
190
response = self.server.store.get_messages(
191
parts[2], query_string)
192
self.send_response(httplib.OK)
194
self.wfile.write(simplejson.dumps(response))
195
except Exception, error:
197
httplib.BAD_REQUEST, error.__doc__)
200
def log_message(self, format, *args):
201
"""Override log_message to use standard Python logging."""
202
message = "%s - - [%s] %s\n" % (
203
self.address_string(), self.log_date_time_string(), format % args)
204
self.logger.info(message)
207
53
class TestPutArchive(TestCase):
209
55
def test_put_archive(self):