1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
|
import json
import logging
import os
import socket
from time import sleep
import pytest
import requests
from errbot.backends.test import FullStackTest, testbot
log = logging.getLogger(__name__)
PYTHONOBJECT = ["foo", {"bar": ("baz", None, 1.0, 2)}]
JSONOBJECT = json.dumps(PYTHONOBJECT)
# Webserver port is picked based on the process ID so that when tests
# are run in parallel with pytest-xdist, each process runs the server
# on a different port
WEBSERVER_PORT = 5000 + (os.getpid() % 1000)
WEBSERVER_SSL_PORT = WEBSERVER_PORT + 1000
def webserver_ready(host, port):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect((host, port))
s.shutdown(socket.SHUT_RDWR)
s.close()
return True
except Exception:
return False
extra_plugin_dir = os.path.join(
os.path.dirname(os.path.realpath(__file__)), "webhooks_plugin"
)
def wait_for_server(port: int):
failure_count = 10
while not webserver_ready("localhost", port):
waiting_time = 1.0 / failure_count
log.info("Webserver not ready yet, sleeping for %f second.", waiting_time)
sleep(waiting_time)
failure_count -= 1
if failure_count == 0:
raise TimeoutError("Could not start the internal Webserver to test.")
@pytest.fixture
def webhook_testbot(request, testbot):
testbot.push_message(
"!plugin config Webserver {'HOST': 'localhost', 'PORT': %s, 'SSL': None}"
% WEBSERVER_PORT
)
log.info(testbot.pop_message())
wait_for_server(WEBSERVER_PORT)
return testbot
def test_not_configured_url_returns_404(webhook_testbot):
assert (
requests.post(
"http://localhost:{}/randomness_blah".format(WEBSERVER_PORT),
"{'toto': 'titui'}",
).status_code
== 404
)
def test_webserver_plugin_ok(webhook_testbot):
assert "/echo" in webhook_testbot.exec_command("!webstatus")
def test_trailing_no_slash_ok(webhook_testbot):
assert requests.post(
"http://localhost:{}/echo".format(WEBSERVER_PORT), JSONOBJECT
).text == repr(json.loads(JSONOBJECT))
def test_trailing_slash_also_ok(webhook_testbot):
assert requests.post(
"http://localhost:{}/echo/".format(WEBSERVER_PORT), JSONOBJECT
).text == repr(json.loads(JSONOBJECT))
def test_json_is_automatically_decoded(webhook_testbot):
assert requests.post(
"http://localhost:{}/webhook1".format(WEBSERVER_PORT), JSONOBJECT
).text == repr(json.loads(JSONOBJECT))
def test_json_on_custom_url_is_automatically_decoded(webhook_testbot):
assert requests.post(
"http://localhost:{}/custom_webhook".format(WEBSERVER_PORT), JSONOBJECT
).text == repr(json.loads(JSONOBJECT))
def test_post_form_on_webhook_without_form_param_is_automatically_decoded(
webhook_testbot,
):
assert requests.post(
"http://localhost:{}/webhook1".format(WEBSERVER_PORT), data=JSONOBJECT
).text == repr(json.loads(JSONOBJECT))
def test_post_form_on_webhook_with_custom_url_and_without_form_param_is_automatically_decoded(
webhook_testbot,
):
assert requests.post(
"http://localhost:{}/custom_webhook".format(WEBSERVER_PORT), data=JSONOBJECT
).text == repr(json.loads(JSONOBJECT))
def test_webhooks_with_form_parameter_decode_json_automatically(webhook_testbot):
form = {"form": JSONOBJECT}
assert requests.post(
"http://localhost:{}/form".format(WEBSERVER_PORT), data=form
).text == repr(json.loads(JSONOBJECT))
def test_webhooks_with_form_parameter_on_custom_url_decode_json_automatically(
webhook_testbot,
):
form = {"form": JSONOBJECT}
assert requests.post(
"http://localhost:{}/custom_form".format(WEBSERVER_PORT), data=form
).text, repr(json.loads(JSONOBJECT))
def test_webhooks_with_raw_request(webhook_testbot):
form = {"form": JSONOBJECT}
assert (
"LocalProxy"
in requests.post(
"http://localhost:{}/raw".format(WEBSERVER_PORT), data=form
).text
)
def test_webhooks_with_naked_decorator_raw_request(webhook_testbot):
form = {"form": JSONOBJECT}
assert (
"LocalProxy"
in requests.post(
"http://localhost:{}/raw2".format(WEBSERVER_PORT), data=form
).text
)
def test_generate_certificate_creates_usable_cert(webhook_testbot):
d = webhook_testbot.bot.bot_config.BOT_DATA_DIR
key_path = os.sep.join((d, "webserver_key.pem"))
cert_path = os.sep.join((d, "webserver_certificate.pem"))
assert "Generating" in webhook_testbot.exec_command(
"!generate_certificate", timeout=1
)
# Generating a certificate could be slow on weak hardware, so keep a safe
# timeout on the first pop_message()
assert "successfully generated" in webhook_testbot.pop_message(timeout=60)
assert "is recommended" in webhook_testbot.pop_message(timeout=1)
assert key_path in webhook_testbot.pop_message(timeout=1)
webserver_config = {
"HOST": "localhost",
"PORT": WEBSERVER_PORT,
"SSL": {
"certificate": cert_path,
"key": key_path,
"host": "localhost",
"port": WEBSERVER_SSL_PORT,
"enabled": True,
},
}
webhook_testbot.push_message(
"!plugin config Webserver {!r}".format(webserver_config)
)
assert "Plugin configuration done." in webhook_testbot.pop_message(timeout=2)
wait_for_server(WEBSERVER_SSL_PORT)
requests.packages.urllib3.disable_warnings(
requests.packages.urllib3.exceptions.InsecureRequestWarning
)
assert (
requests.post(
"https://localhost:{}/webhook1".format(WEBSERVER_SSL_PORT),
JSONOBJECT,
verify=False,
).text
== repr(json.loads(JSONOBJECT))
)
def test_custom_headers_and_status_codes(webhook_testbot):
assert (
requests.post("http://localhost:{}/webhook6".format(WEBSERVER_PORT)).headers[
"X-Powered-By"
]
== "Errbot"
)
assert (
requests.post("http://localhost:{}/webhook7".format(WEBSERVER_PORT)).status_code
== 403
)
def test_lambda_webhook(webhook_testbot):
assert (
requests.post("http://localhost:{}/lambda".format(WEBSERVER_PORT)).status_code
== 200
)
|