File size: 3,959 Bytes
53b2ac9 | 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 | # SPDX-License-Identifier: MIT
"""Tests for the http_bench plugin."""
import http.server
import os
import sys
import threading
import pytest
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "plugins"))
from http_bench import run, _percentile
# ββ Fixtures ββ
@pytest.fixture(scope="module")
def local_http_server():
"""Start a simple HTTP server on a random port."""
handler = http.server.SimpleHTTPRequestHandler
# Suppress log output
class QuietHandler(handler):
def log_message(self, format, *args):
pass
srv = http.server.HTTPServer(("127.0.0.1", 0), QuietHandler)
port = srv.server_address[1]
t = threading.Thread(target=srv.serve_forever, daemon=True)
t.start()
yield port
srv.shutdown()
# ββ _percentile helper ββ
def test_percentile_basic():
data = list(range(1, 101)) # 1..100
assert _percentile(data, 50) == pytest.approx(50.5, abs=1)
assert _percentile(data, 95) == pytest.approx(95.05, abs=1)
assert _percentile(data, 99) == pytest.approx(99.01, abs=1)
def test_percentile_single():
assert _percentile([42.0], 50) == 42.0
assert _percentile([42.0], 99) == 42.0
def test_percentile_empty():
assert _percentile([], 50) == 0.0
# ββ Basic functionality ββ
def test_missing_url():
result = run()
assert "Error" in result
def test_bench_local_server(local_http_server):
result = run(url=f"http://127.0.0.1:{local_http_server}", requests=5, concurrency=2, timeout=5)
assert "Latency" in result
assert "p50" in result
assert "p95" in result
assert "p99" in result
assert "Throughput" in result
assert "Success" in result
def test_bench_reports_failures():
# Port 1 is almost certainly not an HTTP server
result = run(url="http://127.0.0.1:1", requests=2, concurrency=1, timeout=0.5)
assert "Failed" in result
def test_small_request_count(local_http_server):
result = run(url=f"http://127.0.0.1:{local_http_server}", requests=1)
assert "Total:" in result
assert "1" in result
def test_auto_prepend_http(local_http_server):
"""Should auto-prepend http:// if missing."""
result = run(url=f"127.0.0.1:{local_http_server}", requests=2)
assert "Latency" in result or "Failed" in result
def test_clamp_concurrency(local_http_server):
"""Concurrency over 20 should be clamped."""
result = run(url=f"http://127.0.0.1:{local_http_server}", requests=3, concurrency=100)
# Should still work, just clamped to 20
assert "Total:" in result
def test_clamp_requests(local_http_server):
"""Requests over 500 should be clamped."""
result = run(url=f"http://127.0.0.1:{local_http_server}", requests=9999, concurrency=1)
# Should report 500 max
assert "500" in result
# ββ TLS verification ββ
def _captured_contexts(monkeypatch, **run_kwargs):
import ssl
import urllib.error
import http_bench
contexts = []
def fake_urlopen(req, timeout=None, context=None):
contexts.append(context)
raise urllib.error.URLError("stubbed")
monkeypatch.setattr(http_bench.urllib.request, "urlopen", fake_urlopen)
output = run(url="https://example.invalid", requests=2, concurrency=1, **run_kwargs)
assert len(contexts) == 2
return ssl, contexts, output
def test_tls_verified_by_default(monkeypatch):
ssl, contexts, output = _captured_contexts(monkeypatch)
for ctx in contexts:
assert ctx.verify_mode == ssl.CERT_REQUIRED
assert ctx.check_hostname is True
assert "verification disabled" not in output
@pytest.mark.parametrize("flag", [True, "true"])
def test_tls_insecure_opt_in(monkeypatch, flag):
ssl, contexts, output = _captured_contexts(monkeypatch, insecure=flag)
for ctx in contexts:
assert ctx.verify_mode == ssl.CERT_NONE
assert ctx.check_hostname is False
assert "verification disabled" in output
|