quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

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:
Mmeson.build | 8++++++++
Atests/test_https.py | 133+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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())