commit ceb15f5925dc26c3ba28a1fda4419bceea8442ba
parent 686505608efc054236ee1c335da4a657548d5f33
Author: Florian Dold <dold@taler.net>
Date: Sun, 9 Aug 2026 21:45:46 +0200
test: cover HTTPS CA bundle selection
Diffstat:
2 files changed, 141 insertions(+), 0 deletions(-)
diff --git a/meson.build b/meson.build
@@ -308,4 +308,12 @@ if not meson.is_cross_build()
args : [files('tests/test_tart_inputs.js')])
test('wallet-lifecycle', wallet_lifecycle_test,
timeout : 60)
+
+ openssl_exe = find_program('openssl', required : false)
+ if openssl_exe.found()
+ python = import('python').find_installation()
+ test('https', python,
+ args : [files('tests/test_https.py'), qtart_exe],
+ timeout : 60)
+ endif
endif
diff --git a/tests/test_https.py b/tests/test_https.py
@@ -0,0 +1,133 @@
+#!/usr/bin/env python3
+
+import http.server
+import os
+from pathlib import Path
+import shutil
+import ssl
+import subprocess
+import sys
+import tempfile
+import threading
+
+
+class TestHandler(http.server.BaseHTTPRequestHandler):
+ def do_GET(self):
+ body = b"ok"
+ self.send_response(200)
+ self.send_header("Content-Length", str(len(body)))
+ self.end_headers()
+ self.wfile.write(body)
+
+ def log_message(self, format, *args):
+ pass
+
+
+def run_checked(argv):
+ subprocess.run(
+ argv,
+ check=True,
+ stdout=subprocess.DEVNULL,
+ stderr=subprocess.PIPE,
+ text=True,
+ )
+
+
+def make_certificates(openssl, directory):
+ ca_key = directory / "ca.key"
+ ca_cert = directory / "ca.crt"
+ server_key = directory / "server.key"
+ server_csr = directory / "server.csr"
+ server_cert = directory / "server.crt"
+ server_extensions = directory / "server.ext"
+
+ run_checked([
+ openssl, "req", "-x509", "-newkey", "rsa:2048", "-sha256",
+ "-nodes", "-days", "1", "-subj", "/CN=quickjs-tart test CA",
+ "-addext", "basicConstraints=critical,CA:TRUE",
+ "-addext", "keyUsage=critical,keyCertSign,cRLSign",
+ "-keyout", str(ca_key), "-out", str(ca_cert),
+ ])
+ run_checked([
+ openssl, "req", "-new", "-newkey", "rsa:2048", "-sha256",
+ "-nodes", "-subj", "/CN=127.0.0.1", "-keyout", str(server_key),
+ "-out", str(server_csr),
+ ])
+ server_extensions.write_text(
+ "basicConstraints=critical,CA:FALSE\n"
+ "keyUsage=critical,digitalSignature,keyEncipherment\n"
+ "extendedKeyUsage=serverAuth\n"
+ "subjectAltName=IP:127.0.0.1\n",
+ encoding="ascii",
+ )
+ run_checked([
+ openssl, "x509", "-req", "-in", str(server_csr), "-CA",
+ str(ca_cert), "-CAkey", str(ca_key), "-CAcreateserial", "-days",
+ "1", "-sha256", "-extfile", str(server_extensions), "-out",
+ str(server_cert),
+ ])
+ return ca_cert, server_cert, server_key
+
+
+def run_https_probe(qtart, ca_cert, port, ca_environment_variable):
+ marker = f"https-ok-{ca_environment_variable}"
+ expression = (
+ f'os.fetchHttp("https://127.0.0.1:{port}/", {{}}).promise.then('
+ f'(r) => print(r.status === 200 ? "{marker}" : "bad-status"), '
+ '(e) => print("https-error", e));'
+ )
+ environment = os.environ.copy()
+ environment.pop("CURL_CA_BUNDLE", None)
+ environment.pop("SSL_CERT_DIR", None)
+ environment.pop("SSL_CERT_FILE", None)
+ environment[ca_environment_variable] = str(ca_cert)
+ result = subprocess.run(
+ [qtart, "--std", "-e", expression],
+ env=environment,
+ capture_output=True,
+ text=True,
+ timeout=20,
+ )
+ if result.returncode != 0 or marker not in result.stdout:
+ sys.stderr.write(result.stdout)
+ sys.stderr.write(result.stderr)
+ return False
+ return True
+
+
+def main():
+ if len(sys.argv) != 2:
+ print("usage: test_https.py QTART", file=sys.stderr)
+ return 2
+
+ qtart = sys.argv[1]
+ openssl = shutil.which("openssl")
+ if openssl is None:
+ print("openssl is required", file=sys.stderr)
+ return 2
+ with tempfile.TemporaryDirectory(prefix="quickjs-tart-https-") as tmp:
+ ca_cert, server_cert, server_key = make_certificates(
+ openssl,
+ Path(tmp),
+ )
+ server = http.server.ThreadingHTTPServer(("127.0.0.1", 0), TestHandler)
+ tls_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
+ tls_context.load_cert_chain(server_cert, server_key)
+ server.socket = tls_context.wrap_socket(server.socket, server_side=True)
+ server_thread = threading.Thread(target=server.serve_forever)
+ server_thread.start()
+ try:
+ port = server.server_address[1]
+ if not run_https_probe(qtart, ca_cert, port, "SSL_CERT_FILE"):
+ return 1
+ if not run_https_probe(qtart, ca_cert, port, "CURL_CA_BUNDLE"):
+ return 1
+ finally:
+ server.shutdown()
+ server.server_close()
+ server_thread.join()
+ return 0
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())