Files
unslothai--unsloth/studio/backend/tests/test_cloudflare_tunnel.py
T
wehub-resource-sync e93507a09c
Lockfile supply-chain audit / lockfile supply-chain audit (push) Has been cancelled
Windows Studio GGUF CI / GPU prebuilt resolves without Visual Studio (push) Has been cancelled
Windows Studio GGUF CI / setup.ps1 unit tests (VS 2026 / CMake guard) (push) Has been cancelled
Windows Studio GGUF CI / real-VS detection (VS 2022) (push) Has been cancelled
Windows Studio GGUF CI / real-VS detection (VS 2026) (push) Has been cancelled
Windows Studio GGUF CI / VC++ runtime detect + install round-trip (windows-2025-vs2026) (push) Has been cancelled
Windows Studio GGUF CI / VC++ runtime detect + install round-trip (windows-latest) (push) Has been cancelled
Windows Studio Update CI / Studio Updating Tests (push) Has been cancelled
Wheel CI / Wheel build + content sanity + import smoke (push) Has been cancelled
Lint CI / Source lint (Python + shell + YAML + JSON + safety nets) (push) Has been cancelled
MLX CI on Mac M1 / dispatch (push) Has been cancelled
Security audit / advisory audit (pip + npm + cargo) (push) Has been cancelled
Security audit / pip scan-packages :: extras (push) Has been cancelled
Security audit / pip scan-packages :: studio (push) Has been cancelled
Security audit / pip scan-packages :: hf-stack (push) Has been cancelled
Security audit / npm scan-packages (Studio frontend tarballs) (push) Has been cancelled
Security audit / workflow-trigger lint (pull_request_target / cache-poisoning) (push) Has been cancelled
Security audit / pytest tests/security (push) Has been cancelled
Security audit / npm provenance + new install-script diff (push) Has been cancelled
Studio API CI / Studio API & Auth Tests (push) Has been cancelled
Backend CI / (Python 3.10) (push) Has been cancelled
Backend CI / (Python 3.11) (push) Has been cancelled
Backend CI / (Python 3.12) (push) Has been cancelled
Backend CI / (Python 3.13) (push) Has been cancelled
Backend CI / Repo tests (CPU) (push) Has been cancelled
Frontend CI / Frontend build + bundle sanity (push) Has been cancelled
Studio GGUF CI / OpenAI, Anthropic API tests (push) Has been cancelled
Studio GGUF CI / Tool calling Tests (push) Has been cancelled
Studio GGUF CI / JSON, images (push) Has been cancelled
Mac Studio GGUF CI / OpenAI, Anthropic API tests (push) Has been cancelled
Mac Studio GGUF CI / Tool calling Tests (push) Has been cancelled
Mac Studio GGUF CI / JSON, images (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-14) (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-15) (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-26) (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-15-intel) (push) Has been cancelled
Mac Studio API CI / Studio API & Auth Tests (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-26-intel) (push) Has been cancelled
Mac Studio UI CI / Chat UI Tests (push) Has been cancelled
Studio Tauri CI / Tauri Linux debug build (no codesign) (push) Has been cancelled
Mac Studio Update CI / Studio Updating Tests (push) Has been cancelled
Studio UI CI / Chat UI Tests (push) Has been cancelled
Windows Studio API CI / Studio API & Auth Tests (push) Has been cancelled
Windows Studio UI CI / Chat UI Tests (push) Has been cancelled
Studio Update CI / Studio Updating Tests (push) Has been cancelled
Core / Core (HF=default + TRL=default) (push) Has been cancelled
Core / Core (HF=4.57.6 + TRL<1) (push) Has been cancelled
Core / Core (HF=latest + TRL=latest) (push) Has been cancelled
Core / llama.cpp build + smoke (push) Has been cancelled
Windows Studio GGUF CI / OpenAI, Anthropic API tests (push) Has been cancelled
Windows Studio GGUF CI / Tool calling Tests (push) Has been cancelled
Windows Studio GGUF CI / JSON, images (push) Has been cancelled
Windows Studio GGUF CI / Studio install + inference without Visual Studio (push) Has been cancelled
Studio export capability / capability (macos-latest) (push) Has been cancelled
Studio export capability / capability (ubuntu-latest) (push) Has been cancelled
Studio export capability / capability (windows-latest) (push) Has been cancelled
Cross-platform parity / parity (macos-latest) (push) Has been cancelled
Cross-platform parity / parity (windows-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
Studio load-orchestrator CI / test (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:59:56 +08:00

923 lines
31 KiB
Python

# SPDX-License-Identifier: AGPL-3.0-only
# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0
"""Tests for the Cloudflare quick-tunnel helper and run.py wiring.
cloudflare_tunnel.py is stdlib-only (storage_roots is imported lazily), so it
loads via spec_from_file_location without the studio venv. run.py defaults are
checked by AST so we never import its heavy deps (uvicorn/structlog).
"""
import ast
import importlib.util
import io
import os
import sys
import tarfile
import types
from pathlib import Path
import pytest
_BACKEND = Path(__file__).resolve().parent.parent
_CT_PY = _BACKEND / "cloudflare_tunnel.py"
_RUN_PY = _BACKEND / "run.py"
def _load_ct():
spec = importlib.util.spec_from_file_location("cloudflare_tunnel", _CT_PY)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
ct = _load_ct()
# ── URL parsing ──────────────────────────────────────────────────────
def test_url_regex_extracts_and_ignores_noise():
blob = (
"2026-06-11T10:00:00Z INF Thank you for trying Cloudflare Tunnel.\n"
"2026-06-11T10:00:01Z INF Requesting new quick Tunnel on trycloudflare.com...\n"
"2026-06-11T10:00:01Z INF | https://setting-democracy-gathering.trycloudflare.com |\n"
"2026-06-11T10:00:02Z INF Registered tunnel connection https://not-the-url.example.com\n"
)
m = ct._URL_RE.search(blob)
assert m is not None
assert m.group(0) == "https://setting-democracy-gathering.trycloudflare.com"
def test_url_regex_no_match_on_unrelated():
assert ct._URL_RE.search("INF connecting to https://api.cloudflare.com/v4") is None
def test_url_regex_ignores_api_endpoint():
# cloudflared's failure line names its own API host; it must never be taken
# as the tunnel URL (it returns a 404 and is not a quick tunnel).
line = (
'failed to request quick Tunnel: Post "https://api.trycloudflare.com/tunnel": '
"context deadline exceeded"
)
assert ct._URL_RE.search(line) is None
def test_url_regex_skips_api_host_but_matches_real_url():
blob = (
'ERR failed to request quick Tunnel: Post "https://api.trycloudflare.com/tunnel"\n'
"INF | https://brave-mountain-river-clouds.trycloudflare.com |\n"
)
m = ct._URL_RE.search(blob)
assert m is not None
assert m.group(0) == "https://brave-mountain-river-clouds.trycloudflare.com"
# ── asset mapping ────────────────────────────────────────────────────
@pytest.mark.parametrize(
"system,machine,expected",
[
("Linux", "x86_64", ("cloudflared-linux-amd64", False)),
("Linux", "aarch64", ("cloudflared-linux-arm64", False)),
("Darwin", "arm64", ("cloudflared-darwin-arm64.tgz", True)),
("Darwin", "x86_64", ("cloudflared-darwin-amd64.tgz", True)),
("Windows", "AMD64", ("cloudflared-windows-amd64.exe", False)),
("Windows", "x86", ("cloudflared-windows-386.exe", False)),
("Linux", "mips", None),
("Plan9", "x86_64", None),
],
)
def test_asset_name(monkeypatch, system, machine, expected):
monkeypatch.setattr(ct.platform, "system", lambda: system)
monkeypatch.setattr(ct.platform, "machine", lambda: machine)
assert ct._asset_name() == expected
# ── binary discovery ─────────────────────────────────────────────────
def test_find_cloudflared_prefers_path(monkeypatch):
monkeypatch.setattr(ct.shutil, "which", lambda name: "/usr/local/bin/cloudflared")
assert ct.find_cloudflared() == "/usr/local/bin/cloudflared"
def test_find_cloudflared_falls_back_to_cache(monkeypatch, tmp_path):
cached = tmp_path / "cloudflared"
cached.write_text("#!/bin/sh\n")
cached.chmod(0o755)
monkeypatch.setattr(ct.shutil, "which", lambda name: None)
monkeypatch.setattr(ct, "_cache_path", lambda: cached)
assert ct.find_cloudflared() == str(cached)
def test_find_cloudflared_none_when_missing(monkeypatch, tmp_path):
monkeypatch.setattr(ct.shutil, "which", lambda name: None)
monkeypatch.setattr(ct, "_cache_path", lambda: tmp_path / "absent")
assert ct.find_cloudflared() is None
# ── ensure / download ────────────────────────────────────────────────
def test_ensure_downloads_and_chmods_when_missing(monkeypatch, tmp_path):
cached = tmp_path / "cloudflared"
monkeypatch.setattr(ct, "find_cloudflared", lambda: None)
monkeypatch.setattr(ct, "_asset_name", lambda: ("cloudflared-linux-amd64", False))
monkeypatch.setattr(ct, "_cache_path", lambda: cached)
def fake_download(url, dest):
assert url.endswith("/cloudflared-linux-amd64")
dest.write_bytes(b"ELF-ish")
return True
monkeypatch.setattr(ct, "_download", fake_download)
monkeypatch.setattr(ct.sys, "platform", "linux")
path = ct.ensure_cloudflared()
assert path == str(cached)
assert cached.exists()
# Host OS, not monkeypatched ct.sys.platform.
if os.name != "nt":
assert cached.stat().st_mode & 0o111
def test_ensure_returns_none_on_download_failure(monkeypatch, tmp_path):
monkeypatch.setattr(ct, "find_cloudflared", lambda: None)
monkeypatch.setattr(ct, "_asset_name", lambda: ("cloudflared-linux-amd64", False))
monkeypatch.setattr(ct, "_cache_path", lambda: tmp_path / "cloudflared")
monkeypatch.setattr(ct, "_download", lambda url, dest: False)
assert ct.ensure_cloudflared() is None
def test_ensure_returns_none_for_unsupported_arch(monkeypatch, tmp_path):
monkeypatch.setattr(ct, "find_cloudflared", lambda: None)
monkeypatch.setattr(ct, "_asset_name", lambda: None)
monkeypatch.setattr(ct, "_cache_path", lambda: tmp_path / "cloudflared")
assert ct.ensure_cloudflared() is None
def test_download_sets_user_agent(monkeypatch, tmp_path):
import urllib.request
captured = {}
class _Resp:
_sent = False
def __enter__(self):
return self
def __exit__(self, *a):
return False
def read(self, n = -1):
if self._sent:
return b""
self._sent = True
return b"payload"
def fake_urlopen(req, timeout = None):
captured["ua"] = req.get_header("User-agent")
return _Resp()
monkeypatch.setattr(urllib.request, "urlopen", fake_urlopen)
dest = tmp_path / "cloudflared"
assert ct._download("https://github.com/cloudflare/cloudflared/x", dest) is True
assert captured["ua"] == "unsloth-studio" # GitHub CDN 403s the default UA
assert dest.read_bytes() == b"payload"
# ── cross-platform: Windows (.exe), macOS (.tgz) ─────────────────────
def test_cache_path_uses_exe_on_windows(monkeypatch, tmp_path):
import types
fake_sr = types.ModuleType("utils.paths.storage_roots")
fake_sr.studio_bin_root = lambda: tmp_path
monkeypatch.setitem(sys.modules, "utils.paths.storage_roots", fake_sr)
monkeypatch.setattr(ct.sys, "platform", "win32")
assert ct._cache_path() == tmp_path / "cloudflared.exe"
def test_ensure_windows_downloads_exe(monkeypatch, tmp_path):
cached = tmp_path / "cloudflared.exe"
monkeypatch.setattr(ct, "find_cloudflared", lambda: None)
monkeypatch.setattr(ct, "_asset_name", lambda: ("cloudflared-windows-amd64.exe", False))
monkeypatch.setattr(ct, "_cache_path", lambda: cached)
monkeypatch.setattr(ct.sys, "platform", "win32")
def fake_download(url, dest):
assert url.endswith("/cloudflared-windows-amd64.exe")
dest.write_bytes(b"MZ") # PE header magic
return True
monkeypatch.setattr(ct, "_download", fake_download)
# chmod is skipped on Windows; would raise on a path that does not exist yet.
monkeypatch.setattr(ct.os, "chmod", lambda *a, **k: pytest.fail("chmod called on win32"))
assert ct.ensure_cloudflared() == str(cached)
assert cached.read_bytes() == b"MZ"
def test_ensure_macos_extracts_tgz_and_chmods(monkeypatch, tmp_path):
cached = tmp_path / "cloudflared"
monkeypatch.setattr(ct, "find_cloudflared", lambda: None)
monkeypatch.setattr(ct, "_asset_name", lambda: ("cloudflared-darwin-arm64.tgz", True))
monkeypatch.setattr(ct, "_cache_path", lambda: cached)
monkeypatch.setattr(ct.sys, "platform", "darwin")
def fake_download(url, dest):
# dest is cached.with_suffix(".tgz"); write a real archive there.
assert url.endswith("/cloudflared-darwin-arm64.tgz")
with tarfile.open(dest, "w:gz") as tar:
data = b"mach-o"
info = tarfile.TarInfo(name = "cloudflared")
info.size = len(data)
tar.addfile(info, io.BytesIO(data))
return True
monkeypatch.setattr(ct, "_download", fake_download)
path = ct.ensure_cloudflared()
assert path == str(cached)
assert cached.read_bytes() == b"mach-o"
if os.name != "nt":
assert cached.stat().st_mode & 0o111
assert not cached.with_suffix(".tgz").exists() # temp archive cleaned up
# ── .tgz extraction (darwin) ─────────────────────────────────────────
def _make_tgz(
tmp_path,
member_name,
data = b"bin",
):
tgz = tmp_path / "cf.tgz"
with tarfile.open(tgz, "w:gz") as tar:
info = tarfile.TarInfo(name = member_name)
info.size = len(data)
tar.addfile(info, io.BytesIO(data))
return tgz
def test_tgz_extraction_extracts_clean_member(tmp_path):
tgz = _make_tgz(tmp_path, "cloudflared")
dest = tmp_path / "out"
assert ct._extract_tgz_member(tgz, dest) is True
assert dest.read_bytes() == b"bin"
def test_tgz_extraction_rejects_traversal(tmp_path):
tgz = _make_tgz(tmp_path, "../cloudflared")
dest = tmp_path / "out"
assert ct._extract_tgz_member(tgz, dest) is False
assert not dest.exists()
def test_tgz_extraction_missing_member(tmp_path):
tgz = _make_tgz(tmp_path, "README")
dest = tmp_path / "out"
assert ct._extract_tgz_member(tgz, dest) is False
# ── tunnel lifecycle ─────────────────────────────────────────────────
class _FakePopen:
def __init__(self):
self.terminated = False
self.killed = False
self._alive = True
def poll(self):
return None if self._alive else 0
def terminate(self):
self.terminated = True
self._alive = False
def wait(self, timeout = None):
if self._alive:
raise ct.subprocess.TimeoutExpired(cmd = "cloudflared", timeout = timeout)
return 0
def kill(self):
self.killed = True
self._alive = False
def test_stop_terminates_process():
t = ct.CloudflareTunnel(8080, "/bin/cloudflared")
fake = _FakePopen()
t._proc = fake
t.stop()
assert fake.terminated is True
assert t._proc is None
# second stop is a no-op (idempotent)
t.stop()
def test_start_after_stop_does_not_spawn(monkeypatch):
# If stop() lands before start() (a concurrent shutdown in the caller's
# register->start window), start() must NOT spawn a cloudflared process --
# nobody would own it and it would be orphaned.
t = ct.CloudflareTunnel(8080, "/bin/cloudflared")
spawned = []
class _FakeProc:
stdout = None
def poll(self):
return 0
monkeypatch.setattr(ct.subprocess, "Popen", lambda *a, **k: (spawned.append(a), _FakeProc())[1])
t.stop() # proc is None -> no-op terminate, but marks the tunnel stopped
t.start() # must short-circuit before Popen
assert spawned == []
assert t._proc is None
def test_wait_for_ready_times_out_without_blocking():
t = ct.CloudflareTunnel(8080, "/bin/cloudflared")
assert t.wait_for_ready(timeout = 0.05) is None
def _fake_proc(text):
return types.SimpleNamespace(stdout = io.StringIO(text))
def test_reader_captures_url_and_registration():
t = ct.CloudflareTunnel(8080, "/bin/cloudflared")
t._reader(
_fake_proc(
"INF Requesting new quick Tunnel on trycloudflare.com...\n"
"INF | https://words-here-abc.trycloudflare.com |\n"
"INF Registered tunnel connection connIndex=0 protocol=http2\n"
)
)
assert t.url == "https://words-here-abc.trycloudflare.com"
assert t.ready is True
assert t.wait_for_ready(0) == t.url
assert t.error is None # a fully-registered tunnel records no error
def test_reader_url_without_registration_is_not_ready():
# A URL but no "Registered tunnel connection" (e.g. quic control stream
# fails) must not be advertised -- it returns Cloudflare error 1033.
t = ct.CloudflareTunnel(8080, "/bin/cloudflared")
t._reader(
_fake_proc(
"INF | https://words-here-abc.trycloudflare.com |\n"
'ERR failed to serve tunnel connection error="control stream failure"\n'
)
)
assert t.url == "https://words-here-abc.trycloudflare.com"
assert t.ready is False
assert t.wait_for_ready(0) is None
assert t.error == "cloudflared exited before the tunnel connection registered"
def test_reader_handles_none_stdout():
# Popen.stdout can be None; _reader must not crash and must leave the tunnel
# un-ready so wait_for_ready returns None.
t = ct.CloudflareTunnel(8080, "/bin/cloudflared")
t._reader(types.SimpleNamespace(stdout = None))
assert t.url is None
assert t.ready is False
assert t.wait_for_ready(0) is None
assert t.error == "cloudflared exited before emitting a tunnel URL"
def test_reader_ignores_api_endpoint_failure_line():
t = ct.CloudflareTunnel(8080, "/bin/cloudflared")
t._reader(
_fake_proc(
"ERR failed to request quick Tunnel: Post "
'"https://api.trycloudflare.com/tunnel": context deadline exceeded\n'
)
)
assert t.url is None
assert t.wait_for_ready(0) is None
assert t.error == "cloudflared exited before emitting a tunnel URL"
def test_start_studio_tunnel_no_binary(monkeypatch):
monkeypatch.setattr(ct, "ensure_cloudflared", lambda: None)
assert ct.start_studio_tunnel(8080) is None
def test_start_studio_tunnel_registers_before_wait(monkeypatch):
# The tunnel must be visible to stop_studio_tunnel() during the readiness
# wait, else a shutdown in that window orphans cloudflared.
seen = {}
class _Stub:
def __init__(
self,
port,
binary,
protocol = None,
):
self.url = None
def start(self):
pass
def wait_for_ready(self, timeout):
seen["active_during_wait"] = ct._active_tunnel is self
self.url = "https://x.trycloudflare.com"
return self.url
def stop(self):
seen["stopped"] = True
monkeypatch.setattr(ct, "ensure_cloudflared", lambda: "/bin/cloudflared")
monkeypatch.setattr(ct, "CloudflareTunnel", _Stub)
try:
assert ct.start_studio_tunnel(8080) == "https://x.trycloudflare.com"
assert seen["active_during_wait"] is True
finally:
ct.stop_studio_tunnel()
def test_start_studio_tunnel_clears_and_stops_on_no_url(monkeypatch):
seen = {}
class _Stub:
def __init__(
self,
port,
binary,
protocol = None,
):
self.url = None
def start(self):
pass
def wait_for_ready(self, timeout):
return None
def stop(self):
seen["stopped"] = True
monkeypatch.setattr(ct, "ensure_cloudflared", lambda: "/bin/cloudflared")
monkeypatch.setattr(ct, "CloudflareTunnel", _Stub)
assert ct.start_studio_tunnel(8080) is None
assert seen.get("stopped") is True
assert ct._active_tunnel is None
def test_start_studio_tunnel_returns_url(monkeypatch):
class _StubTunnel:
def __init__(
self,
port,
binary,
protocol = None,
):
self.url = None
def start(self):
self.url = "https://stub-xyz.trycloudflare.com"
def wait_for_ready(self, timeout):
return self.url
def stop(self):
pass
monkeypatch.setattr(ct, "ensure_cloudflared", lambda: "/bin/cloudflared")
monkeypatch.setattr(ct, "CloudflareTunnel", _StubTunnel)
try:
assert ct.start_studio_tunnel(8080) == "https://stub-xyz.trycloudflare.com"
finally:
ct.stop_studio_tunnel()
def test_start_studio_tunnel_falls_back_to_http2(monkeypatch):
# First attempt mints a URL but never registers (quic blocked); the http2
# retry registers and wins.
attempts = []
class _Stub:
def __init__(
self,
port,
binary,
protocol = None,
):
self.protocol = protocol
self.url = None
attempts.append(protocol)
def start(self):
self.url = "https://words.trycloudflare.com" # URL always minted
def wait_for_ready(self, timeout):
return self.url if self.protocol == "http2" else None
def stop(self):
pass
monkeypatch.setattr(ct, "ensure_cloudflared", lambda: "/bin/cloudflared")
monkeypatch.setattr(ct, "CloudflareTunnel", _Stub)
try:
assert ct.start_studio_tunnel(8080) == "https://words.trycloudflare.com"
assert attempts == [None, "http2"] # default first, then forced http2
finally:
ct.stop_studio_tunnel()
def test_start_studio_tunnel_no_retry_when_shutdown_between_attempts(monkeypatch):
# A stop() landing in the gap AFTER the failed first attempt is cleaned up but
# BEFORE the http2 retry registers must abort the loop -- not start a second
# tunnel that nobody will ever stop (Codex review). Simulated by having the
# first attempt's stop() (called during cleanup) trigger the shutdown.
attempts = []
class _Stub:
def __init__(
self,
port,
binary,
protocol = None,
):
self.url = None
attempts.append(protocol)
def start(self):
self.url = "https://words.trycloudflare.com" # URL minted, never ready
def wait_for_ready(self, timeout):
return None
def stop(self):
ct.stop_studio_tunnel() # a concurrent shutdown lands in the gap
monkeypatch.setattr(ct, "ensure_cloudflared", lambda: "/bin/cloudflared")
monkeypatch.setattr(ct, "CloudflareTunnel", _Stub)
assert ct.start_studio_tunnel(8080) is None
assert attempts == [None] # http2 retry aborted after shutdown
assert ct._active_tunnel is None
def test_start_studio_tunnel_no_http2_retry_when_no_url(monkeypatch):
# No URL at all is an API/network failure; the http2 fallback would not help,
# so it must be skipped (don't burn a second timeout window).
attempts = []
class _Stub:
def __init__(
self,
port,
binary,
protocol = None,
):
self.url = None
attempts.append(protocol)
def start(self):
pass # never mints a URL
def wait_for_ready(self, timeout):
return None
def stop(self):
pass
monkeypatch.setattr(ct, "ensure_cloudflared", lambda: "/bin/cloudflared")
monkeypatch.setattr(ct, "CloudflareTunnel", _Stub)
assert ct.start_studio_tunnel(8080) is None
assert attempts == [None]
def test_start_studio_tunnel_both_protocols_fail_registration(monkeypatch):
# Both quic and http2 mint a URL but neither registers -> both attempts are
# exhausted and None is returned (no dead URL advertised).
attempts = []
class _Stub:
def __init__(
self,
port,
binary,
protocol = None,
):
self.url = None
attempts.append(protocol)
def start(self):
self.url = "https://words.trycloudflare.com" # URL minted, never ready
def wait_for_ready(self, timeout):
return None
def stop(self):
pass
monkeypatch.setattr(ct, "ensure_cloudflared", lambda: "/bin/cloudflared")
monkeypatch.setattr(ct, "CloudflareTunnel", _Stub)
assert ct.start_studio_tunnel(8080) is None
assert attempts == [None, "http2"]
assert ct._active_tunnel is None
def test_start_studio_tunnel_aborts_retry_on_concurrent_shutdown(monkeypatch):
# If a concurrent stop_studio_tunnel() clears _active_tunnel while we wait,
# the retry loop must NOT start a second (http2) tunnel: shutdown is already
# done, so nothing would ever stop it and it would be orphaned.
attempts = []
class _Stub:
def __init__(
self,
port,
binary,
protocol = None,
):
self.url = None
attempts.append(protocol)
def start(self):
self.url = "https://words.trycloudflare.com" # URL minted (saw_url True)
def wait_for_ready(self, timeout):
# Simulate stop_studio_tunnel() landing during the wait.
with ct._active_lock:
ct._active_tunnel = None
return None # never registered
def stop(self):
pass
monkeypatch.setattr(ct, "ensure_cloudflared", lambda: "/bin/cloudflared")
monkeypatch.setattr(ct, "CloudflareTunnel", _Stub)
assert ct.start_studio_tunnel(8080) is None
assert attempts == [None] # no http2 retry -> no orphaned second tunnel
assert ct._active_tunnel is None
# ── run.py source-level pins (AST / source, no heavy import) ─────────
def _func_param_defaults(source, func_name):
tree = ast.parse(source)
for node in ast.walk(tree):
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) and node.name == func_name:
args = node.args.args
defaults = node.args.defaults
offset = len(args) - len(defaults)
out = {}
for i, d in enumerate(defaults):
if isinstance(d, ast.Constant):
out[args[offset + i].arg] = d.value
return out
return {}
def _argparse_default(source, option):
tree = ast.parse(source)
for node in ast.walk(tree):
if isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute):
if node.func.attr == "add_argument" and node.args:
a0 = node.args[0]
if isinstance(a0, ast.Constant) and a0.value == option:
for kw in node.keywords:
if kw.arg == "default" and isinstance(kw.value, ast.Constant):
return kw.value.value
return None
def test_run_server_cloudflare_default_true():
defaults = _func_param_defaults(_RUN_PY.read_text(), "run_server")
assert defaults.get("cloudflare") is True
def test_argparse_cloudflare_default_true():
assert _argparse_default(_RUN_PY.read_text(), "--cloudflare") is True
def test_verify_global_reachability_marks_private_address_unreachable():
src = _RUN_PY.read_text()
tree = ast.parse(src)
func_src = next(
ast.get_source_segment(src, n)
for n in ast.walk(tree)
if isinstance(n, ast.FunctionDef) and n.name == "_verify_global_reachability"
)
captured = []
ns = {
"_public_reachable": None,
"_stdout_color_ok": lambda: False,
"_url_host": lambda host: host,
"print": lambda *a, **k: captured.append(" ".join(str(x) for x in a)),
}
exec(compile(func_src, "<verify_global_reachability>", "exec"), ns)
ns["_verify_global_reachability"]("192.168.1.10", 8888)
assert ns["_public_reachable"] is False
assert "private/LAN address" in "\n".join(captured)
def test_run_server_registers_tunnel_atexit_backstop():
# An abnormal exit (exception after startup -> sys.exit) bypasses
# _graceful_shutdown; an atexit backstop must still stop the tunnel.
src = _RUN_PY.read_text()
assert "atexit.register(stop_studio_tunnel)" in src
def _run_print_cloudflare_line(
monkeypatch,
*,
cloudflare_url,
public_reachable,
cloudflare_requested = False,
cloudflare_flag = True,
secure = False,
loopback_host = "127.0.0.1",
color = False,
):
"""Exec _print_cloudflare_line without importing run.py's heavy deps."""
src = _RUN_PY.read_text()
tree = ast.parse(src)
func_src = next(
ast.get_source_segment(src, n)
for n in ast.walk(tree)
if isinstance(n, ast.FunctionDef) and n.name == "_print_cloudflare_line"
)
stub = types.ModuleType("startup_banner")
stub.stdout_supports_color = lambda: color
monkeypatch.setitem(sys.modules, "startup_banner", stub)
captured: list[str] = []
ns = {
"_cloudflare_url": cloudflare_url,
"_public_reachable": public_reachable,
"_cloudflare_requested": cloudflare_requested,
"_cloudflare_flag": cloudflare_flag,
"print": lambda *a, **k: captured.append(" ".join(str(x) for x in a)),
}
exec(compile(func_src, "<print_cloudflare_line>", "exec"), ns)
ns["_print_cloudflare_line"](secure = secure, loopback_host = loopback_host)
return "\n".join(captured)
def test_cloudflare_line_reworded_when_public_unreachable(monkeypatch):
out = _run_print_cloudflare_line(
monkeypatch, cloudflare_url = "https://x.trycloudflare.com", public_reachable = False
)
assert "Use the secure link access via Cloudflare instead: https://x.trycloudflare.com" in out
def test_cloudflare_line_default_wording_when_reachable(monkeypatch):
out = _run_print_cloudflare_line(
monkeypatch, cloudflare_url = "https://x.trycloudflare.com", public_reachable = True
)
assert "Secure link access via Cloudflare: https://x.trycloudflare.com" in out
assert "Use the secure link" not in out
def test_cloudflare_line_default_wording_when_unknown(monkeypatch):
out = _run_print_cloudflare_line(
monkeypatch, cloudflare_url = "https://x.trycloudflare.com", public_reachable = None
)
assert "Secure link access via Cloudflare: https://x.trycloudflare.com" in out
assert "Use the secure link" not in out
def test_cloudflare_line_states_inactive_when_enabled_but_not_requested(monkeypatch):
out = _run_print_cloudflare_line(monkeypatch, cloudflare_url = None, public_reachable = False)
assert "Cloudflare tunnel: OFF for this mode" in out
assert "local network only" in out
def test_cloudflare_line_warns_when_public_url_up(monkeypatch):
out = _run_print_cloudflare_line(
monkeypatch,
cloudflare_url = "https://x.trycloudflare.com",
public_reachable = True,
cloudflare_requested = True,
)
assert "Secure link access via Cloudflare: https://x.trycloudflare.com" in out
assert "Cloudflare tunnel: ON" in out
assert "PUBLIC" in out
assert "--no-cloudflare" in out
assert "raw port is also publicly reachable" in out
assert "local network only" not in out
def test_cloudflare_line_secure_mode_suppresses_public_warning(monkeypatch):
out = _run_print_cloudflare_line(
monkeypatch,
cloudflare_url = "https://x.trycloudflare.com",
public_reachable = True,
cloudflare_requested = True,
secure = True,
)
assert "Secure link access via Cloudflare: https://x.trycloudflare.com" in out
assert "Cloudflare tunnel: ON" not in out
def test_cloudflare_line_states_disabled_when_off(monkeypatch):
out = _run_print_cloudflare_line(
monkeypatch,
cloudflare_url = None,
public_reachable = False,
cloudflare_requested = False,
cloudflare_flag = False,
)
assert "Cloudflare tunnel: OFF" in out
assert "local network only" in out
def test_cloudflare_line_states_failed_when_requested_but_no_url(monkeypatch):
out = _run_print_cloudflare_line(
monkeypatch,
cloudflare_url = None,
public_reachable = False,
cloudflare_requested = True,
cloudflare_flag = True,
)
assert "requested but failed to start" in out
assert "local network only" in out
def test_cloudflare_line_off_does_not_claim_local_only_when_unknown(monkeypatch):
out = _run_print_cloudflare_line(
monkeypatch,
cloudflare_url = None,
public_reachable = None,
cloudflare_requested = False,
cloudflare_flag = False,
)
assert "Cloudflare tunnel: OFF" in out
assert "Raw port reachability was not verified" in out
assert "local network only" not in out
def test_cloudflare_line_failed_does_not_claim_local_only_when_unknown(monkeypatch):
out = _run_print_cloudflare_line(
monkeypatch,
cloudflare_url = None,
public_reachable = None,
cloudflare_requested = True,
cloudflare_flag = True,
)
assert "requested but failed to start" in out
assert "Raw port reachability was not verified" in out
assert "local network only" not in out
@pytest.mark.parametrize(
"cloudflare_requested,cloudflare_flag,expected",
[
(True, True, "requested but failed to start"),
(False, True, "Cloudflare tunnel: OFF for this mode"),
(False, False, "Cloudflare tunnel: OFF"),
],
)
def test_cloudflare_line_unknown_warns_with_loopback_host(
monkeypatch, cloudflare_requested, cloudflare_flag, expected
):
out = _run_print_cloudflare_line(
monkeypatch,
cloudflare_url = None,
public_reachable = None,
cloudflare_requested = cloudflare_requested,
cloudflare_flag = cloudflare_flag,
loopback_host = "::1",
color = True,
)
assert expected in out
assert "bind ::1" in out
assert "bind 127.0.0.1" not in out
assert "\033[38;5;215;1m" in out
def test_cloudflare_line_off_does_not_claim_local_only_when_publicly_reachable(monkeypatch):
out = _run_print_cloudflare_line(
monkeypatch,
cloudflare_url = None,
public_reachable = True,
cloudflare_requested = False,
cloudflare_flag = False,
)
assert "Cloudflare tunnel: OFF" in out
assert "reachable from the public internet" in out
assert "local network only" not in out
def test_cloudflare_line_failed_does_not_claim_local_only_when_publicly_reachable(monkeypatch):
out = _run_print_cloudflare_line(
monkeypatch,
cloudflare_url = None,
public_reachable = True,
cloudflare_requested = True,
cloudflare_flag = True,
)
assert "requested but failed to start" in out
assert "reachable from the public internet" in out
assert "local network only" not in out