Skip to content

Commit 915f9a0

Browse files
committed
Testing: Refactor support code out of zope.testing entrypoint
`tests.py` is the entrypoint file that will be used by `zope.testing` to discover the test cases on behalf of what's returned from `test_suite`. It is better to not overload it with other support code that may also be needed in other contexts.
1 parent 43db4a9 commit 915f9a0

File tree

2 files changed

+284
-284
lines changed

2 files changed

+284
-284
lines changed

src/crate/client/test_support.py

+273
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
# -*- coding: utf-8; -*-
2+
#
3+
# Licensed to CRATE Technology GmbH ("Crate") under one or more contributor
4+
# license agreements. See the NOTICE file distributed with this work for
5+
# additional information regarding copyright ownership. Crate licenses
6+
# this file to you under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License. You may
8+
# obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15+
# License for the specific language governing permissions and limitations
16+
# under the License.
17+
#
18+
# However, if you have executed another commercial license agreement
19+
# with Crate these terms will supersede the license and you may use the
20+
# software solely pursuant to the terms of the relevant commercial agreement.
21+
22+
from __future__ import absolute_import
23+
24+
import json
25+
import os
26+
import socket
27+
import unittest
28+
from pprint import pprint
29+
from http.server import HTTPServer, BaseHTTPRequestHandler
30+
import ssl
31+
import time
32+
import threading
33+
import logging
34+
35+
import stopit
36+
37+
from crate.testing.layer import CrateLayer
38+
from crate.testing.settings import \
39+
crate_host, crate_path, crate_port, \
40+
crate_transport_port, docs_path, localhost
41+
from crate.client import connect
42+
43+
44+
makeSuite = unittest.TestLoader().loadTestsFromTestCase
45+
46+
log = logging.getLogger('crate.testing.layer')
47+
ch = logging.StreamHandler()
48+
ch.setLevel(logging.ERROR)
49+
log.addHandler(ch)
50+
51+
52+
def cprint(s):
53+
if isinstance(s, bytes):
54+
s = s.decode('utf-8')
55+
print(s)
56+
57+
58+
settings = {
59+
'udc.enabled': 'false',
60+
'lang.js.enabled': 'true',
61+
'auth.host_based.enabled': 'true',
62+
'auth.host_based.config.0.user': 'crate',
63+
'auth.host_based.config.0.method': 'trust',
64+
'auth.host_based.config.98.user': 'trusted_me',
65+
'auth.host_based.config.98.method': 'trust',
66+
'auth.host_based.config.99.user': 'me',
67+
'auth.host_based.config.99.method': 'password',
68+
}
69+
crate_layer = None
70+
71+
72+
def ensure_cratedb_layer():
73+
"""
74+
In order to skip individual tests by manually disabling them within
75+
`def test_suite()`, it is crucial make the test layer not run on each
76+
and every occasion. So, things like this will be possible::
77+
78+
./bin/test -vvvv --ignore_dir=testing
79+
80+
TODO: Through a subsequent patch, the possibility to individually
81+
unselect specific tests might be added to `def test_suite()`
82+
on behalf of environment variables.
83+
A blueprint for this kind of logic can be found at
84+
https://github.com/crate/crate/commit/414cd833.
85+
"""
86+
global crate_layer
87+
88+
if crate_layer is None:
89+
crate_layer = CrateLayer('crate',
90+
crate_home=crate_path(),
91+
port=crate_port,
92+
host=localhost,
93+
transport_port=crate_transport_port,
94+
settings=settings)
95+
return crate_layer
96+
97+
98+
def setUpCrateLayerBaseline(test):
99+
if hasattr(test, "globs"):
100+
test.globs['crate_host'] = crate_host
101+
test.globs['pprint'] = pprint
102+
test.globs['print'] = cprint
103+
104+
with connect(crate_host) as conn:
105+
cursor = conn.cursor()
106+
107+
with open(docs_path('testing/testdata/mappings/locations.sql')) as s:
108+
stmt = s.read()
109+
cursor.execute(stmt)
110+
stmt = ("select count(*) from information_schema.tables "
111+
"where table_name = 'locations'")
112+
cursor.execute(stmt)
113+
assert cursor.fetchall()[0][0] == 1
114+
115+
data_path = docs_path('testing/testdata/data/test_a.json')
116+
# load testing data into crate
117+
cursor.execute("copy locations from ?", (data_path,))
118+
# refresh location table so imported data is visible immediately
119+
cursor.execute("refresh table locations")
120+
# create blob table
121+
cursor.execute("create blob table myfiles clustered into 1 shards " +
122+
"with (number_of_replicas=0)")
123+
124+
# create users
125+
cursor.execute("CREATE USER me WITH (password = 'my_secret_pw')")
126+
cursor.execute("CREATE USER trusted_me")
127+
128+
cursor.close()
129+
130+
131+
def tearDownDropEntitiesBaseline(test):
132+
"""
133+
Drop all tables, views, and users created by `setUpWithCrateLayer*`.
134+
"""
135+
ddl_statements = [
136+
"DROP TABLE foobar",
137+
"DROP TABLE locations",
138+
"DROP BLOB TABLE myfiles",
139+
"DROP USER me",
140+
"DROP USER trusted_me",
141+
]
142+
_execute_statements(ddl_statements)
143+
144+
145+
class HttpsTestServerLayer:
146+
PORT = 65534
147+
HOST = "localhost"
148+
CERT_FILE = os.path.abspath(os.path.join(os.path.dirname(__file__),
149+
"pki/server_valid.pem"))
150+
CACERT_FILE = os.path.abspath(os.path.join(os.path.dirname(__file__),
151+
"pki/cacert_valid.pem"))
152+
153+
__name__ = "httpsserver"
154+
__bases__ = tuple()
155+
156+
class HttpsServer(HTTPServer):
157+
def get_request(self):
158+
159+
# Prepare SSL context.
160+
context = ssl._create_unverified_context(
161+
protocol=ssl.PROTOCOL_TLS_SERVER,
162+
cert_reqs=ssl.CERT_OPTIONAL,
163+
check_hostname=False,
164+
purpose=ssl.Purpose.CLIENT_AUTH,
165+
certfile=HttpsTestServerLayer.CERT_FILE,
166+
keyfile=HttpsTestServerLayer.CERT_FILE,
167+
cafile=HttpsTestServerLayer.CACERT_FILE)
168+
169+
# Set minimum protocol version, TLSv1 and TLSv1.1 are unsafe.
170+
context.minimum_version = ssl.TLSVersion.TLSv1_2
171+
172+
# Wrap TLS encryption around socket.
173+
socket, client_address = HTTPServer.get_request(self)
174+
socket = context.wrap_socket(socket, server_side=True)
175+
176+
return socket, client_address
177+
178+
class HttpsHandler(BaseHTTPRequestHandler):
179+
180+
payload = json.dumps({"name": "test", "status": 200, })
181+
182+
def do_GET(self):
183+
self.send_response(200)
184+
payload = self.payload.encode('UTF-8')
185+
self.send_header("Content-Length", len(payload))
186+
self.send_header("Content-Type", "application/json; charset=UTF-8")
187+
self.end_headers()
188+
self.wfile.write(payload)
189+
190+
def setUp(self):
191+
self.server = self.HttpsServer(
192+
(self.HOST, self.PORT),
193+
self.HttpsHandler
194+
)
195+
thread = threading.Thread(target=self.serve_forever)
196+
thread.daemon = True # quit interpreter when only thread exists
197+
thread.start()
198+
self.waitForServer()
199+
200+
def serve_forever(self):
201+
print("listening on", self.HOST, self.PORT)
202+
self.server.serve_forever()
203+
print("server stopped.")
204+
205+
def tearDown(self):
206+
self.server.shutdown()
207+
self.server.server_close()
208+
209+
def isUp(self):
210+
"""
211+
Test if a host is up.
212+
"""
213+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
214+
ex = s.connect_ex((self.HOST, self.PORT))
215+
s.close()
216+
return ex == 0
217+
218+
def waitForServer(self, timeout=5):
219+
"""
220+
Wait for the host to be available.
221+
"""
222+
with stopit.ThreadingTimeout(timeout) as to_ctx_mgr:
223+
while True:
224+
if self.isUp():
225+
break
226+
time.sleep(0.001)
227+
228+
if not to_ctx_mgr:
229+
raise TimeoutError("Could not properly start embedded webserver "
230+
"within {} seconds".format(timeout))
231+
232+
233+
def setUpWithHttps(test):
234+
test.globs['crate_host'] = "https://{0}:{1}".format(
235+
HttpsTestServerLayer.HOST, HttpsTestServerLayer.PORT
236+
)
237+
test.globs['pprint'] = pprint
238+
test.globs['print'] = cprint
239+
240+
test.globs['cacert_valid'] = os.path.abspath(
241+
os.path.join(os.path.dirname(__file__), "pki/cacert_valid.pem")
242+
)
243+
test.globs['cacert_invalid'] = os.path.abspath(
244+
os.path.join(os.path.dirname(__file__), "pki/cacert_invalid.pem")
245+
)
246+
test.globs['clientcert_valid'] = os.path.abspath(
247+
os.path.join(os.path.dirname(__file__), "pki/client_valid.pem")
248+
)
249+
test.globs['clientcert_invalid'] = os.path.abspath(
250+
os.path.join(os.path.dirname(__file__), "pki/client_invalid.pem")
251+
)
252+
253+
254+
def _execute_statements(statements, on_error="ignore"):
255+
with connect(crate_host) as conn:
256+
cursor = conn.cursor()
257+
for stmt in statements:
258+
_execute_statement(cursor, stmt, on_error=on_error)
259+
cursor.close()
260+
261+
262+
def _execute_statement(cursor, stmt, on_error="ignore"):
263+
try:
264+
cursor.execute(stmt)
265+
except Exception: # pragma: no cover
266+
# FIXME: Why does this croak on statements like ``DROP TABLE cities``?
267+
# Note: When needing to debug the test environment, you may want to
268+
# enable this logger statement.
269+
# log.exception("Executing SQL statement failed")
270+
if on_error == "ignore":
271+
pass
272+
elif on_error == "raise":
273+
raise

0 commit comments

Comments
 (0)