All checks were successful
Desktop source guards / guards (push) Successful in 5s
Скрипт выпуска и CI запускают каждый гард из release_guards.txt как
`python <файл>` и смотрят только на код возврата. Семь файлов не вызывали
свои test_* вовсе и проходили, ничего не проверив, а в трёх ручной список
вызовов отстал на один новый тест. Теперь каждый гард вызывает все свои
test_* сам, тем же блоком, что уже стоял в test_web_proxy_carrier_flow.py,
а новый гард test_release_guards_run_their_tests.py следит, чтобы так
оставалось у всех файлов списка.
Под запуском всплыли устаревшие проверки, сломанные коммитом 744109cc78
(«Pace MTProxy dials instead of steering them»):
- он намеренно убрал подстройку MTProxy под кэш возможностей и
принудительный ChromeModern: профиль ClientHello остаётся тем, что выбрал
пользователь. Проверки в test_mtproxy_target_flow.py и
test_proxy_capability_cache.py требовали старое поведение и теперь
требуют новое: в ветке MTProxy нет обращения к кэшу, а
BoringMtproxyStealthOptions сохраняет выбранный профиль;
- reportMtproxySuccess удалён, поэтому проверка «первые данные отмечаются
один раз на сокет» смотрит на то, что сейчас стоит под этой защитой:
прогресс, событие диагностики и noteDataPathSuccess;
- память маршрутов он оставил, но код переписан: проверка порядка
(ответившие адреса, затем неопробованные, затем неудачные) идёт по
нынешнему виду routeOrder.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
153 lines
5.3 KiB
Python
153 lines
5.3 KiB
Python
from pathlib import Path
|
|
from session_private_sources import read_session_private_sources
|
|
|
|
|
|
SOURCE_DIR = Path(__file__).resolve().parents[1]
|
|
MTPROXY_DIR = SOURCE_DIR / "mtproto" / "proxy" / "mtproxy"
|
|
RUNTIME_DIR = SOURCE_DIR / "mtproto" / "runtime"
|
|
ENDPOINT_IDENTITY_H = MTPROXY_DIR / "endpoint_identity.h"
|
|
TLS_SOCKET_H = MTPROXY_DIR / "tls_socket.h"
|
|
TLS_SOCKET_CPP = MTPROXY_DIR / "tls_socket.cpp"
|
|
STATUS_H = SOURCE_DIR / "mtproto" / "proxy" / "status.h"
|
|
STATUS_CPP = SOURCE_DIR / "mtproto" / "proxy" / "status.cpp"
|
|
DIAGNOSTICS_CPP = SOURCE_DIR / "mtproto" / "proxy" / "diagnostics.cpp"
|
|
SESSION_CPP = SOURCE_DIR / "mtproto" / "session" / "private" / "session_private.cpp"
|
|
CHECK_CPP = SOURCE_DIR / "mtproto" / "proxy" / "check.cpp"
|
|
PROXY_ENDPOINT_H = RUNTIME_DIR / "proxy_endpoint.h"
|
|
CONNECTION_STATUS_TYPES_H = RUNTIME_DIR / "connection_status_types.h"
|
|
LANG = SOURCE_DIR.parent / "Resources" / "langs" / "lang.strings"
|
|
CONNECTING_WIDGET = SOURCE_DIR / "window" / "window_connecting_widget.cpp"
|
|
|
|
|
|
def read(path):
|
|
assert path.exists(), f"missing expected source file: {path}"
|
|
return path.read_text(encoding="utf-8")
|
|
|
|
|
|
def function_body(source, signature):
|
|
start = source.index(signature)
|
|
brace = source.index("{", start)
|
|
depth = 0
|
|
for i in range(brace, len(source)):
|
|
if source[i] == "{":
|
|
depth += 1
|
|
elif source[i] == "}":
|
|
depth -= 1
|
|
if depth == 0:
|
|
return source[brace:i + 1]
|
|
raise AssertionError(f"function body not found: {signature}")
|
|
|
|
|
|
def test_failure_reason_enum_is_phase_specific():
|
|
header = read(PROXY_ENDPOINT_H)
|
|
|
|
assert "enum class FailureReason" in header
|
|
assert header.index("enum class FailureReason") < (
|
|
header.index("struct CanonicalProxyEndpoint"))
|
|
for reason in (
|
|
"DnsFailed",
|
|
"TcpConnectTimeout",
|
|
"TcpConnectedNoClientHelloWrite",
|
|
"ClientHelloSentNoServerHello",
|
|
"TlsAlertAfterClientHello",
|
|
"ServerHelloHmacMismatch",
|
|
"ServerHelloOkNoAppData",
|
|
"AppDataRemoteClosed",
|
|
"ProxyProtocolBadResponse",
|
|
):
|
|
assert reason in header
|
|
|
|
for old_reason in (
|
|
"\tDnsHostNotFound,",
|
|
"\tNoServerHelloAfterClientHello,",
|
|
"\tPostHandshakeNoAppData,",
|
|
"\tRemoteClosed,",
|
|
"\tTimeout,",
|
|
"\tBadResponse,",
|
|
):
|
|
assert old_reason not in header
|
|
|
|
|
|
def test_tls_socket_reports_timeout_by_handshake_phase():
|
|
header = read(TLS_SOCKET_H)
|
|
source = read(TLS_SOCKET_CPP)
|
|
failure_body = function_body(
|
|
source,
|
|
"MtProxy::FailureReason TlsSocket::failureReason() const")
|
|
error_body = function_body(source, "void TlsSocket::handleError(int errorCode)")
|
|
|
|
assert "bool _firstAppDataReceived = false;" in header
|
|
assert "MtProxy::FailureReason::TcpConnectTimeout" in failure_body
|
|
assert "MtProxy::FailureReason::TcpConnectedNoClientHelloWrite" in failure_body
|
|
assert "MtProxy::FailureReason::ClientHelloSentNoServerHello" in failure_body
|
|
assert "MtProxy::FailureReason::ServerHelloOkNoAppData" in failure_body
|
|
assert "MtProxy::FailureReason::AppDataRemoteClosed" in error_body
|
|
assert "PostHandshakeNoAppData" not in failure_body
|
|
assert "TcpNotConnected" not in failure_body
|
|
|
|
|
|
def trait_row(traits_body, reason):
|
|
segment = traits_body.split(f"case FailureReason::{reason}:", 1)[1]
|
|
return segment.split("case FailureReason::", 1)[0]
|
|
|
|
|
|
def test_logs_and_proxy_status_use_phase_specific_names():
|
|
status_h = read(STATUS_H)
|
|
status_types = read(CONNECTION_STATUS_TYPES_H)
|
|
status_cpp = read(STATUS_CPP)
|
|
diagnostics = read(DIAGNOSTICS_CPP)
|
|
widget = read(CONNECTING_WIDGET)
|
|
lang = read(LANG)
|
|
|
|
for reason in (
|
|
"DnsFailed",
|
|
"TcpConnectTimeout",
|
|
"TcpConnectedNoClientHelloWrite",
|
|
"ClientHelloSentNoServerHello",
|
|
"ServerHelloOkNoAppData",
|
|
"AppDataRemoteClosed",
|
|
"ProxyProtocolBadResponse",
|
|
):
|
|
assert f"ProxyMtproxyTerminalReason::{reason}" in status_cpp
|
|
assert reason in status_types
|
|
|
|
for diagnostic in (
|
|
"dns_failed",
|
|
"tcp_connect_timeout",
|
|
"tcp_connected_no_client_hello_write",
|
|
"client_hello_sent_no_server_hello",
|
|
"server_hello_ok_no_appdata",
|
|
"appdata_remote_closed",
|
|
"proxy_protocol_bad_response",
|
|
):
|
|
assert diagnostic in diagnostics
|
|
|
|
for kind in (
|
|
"MtproxyDnsFailed",
|
|
"MtproxyTcpConnectTimeout",
|
|
"MtproxyTcpConnectedNoClientHelloWrite",
|
|
"MtproxyServerHelloOkNoAppData",
|
|
"MtproxyAppDataRemoteClosed",
|
|
"MtproxyProxyProtocolBadResponse",
|
|
):
|
|
assert f"ProxyConnectionStatusKind::{kind}" in status_cpp
|
|
assert kind in status_h
|
|
assert kind in widget
|
|
|
|
for key in (
|
|
"lng_proxy_status_mtproxy_dns_failed",
|
|
"lng_proxy_status_mtproxy_tcp_timeout",
|
|
"lng_proxy_status_mtproxy_tcp_no_client_hello",
|
|
"lng_proxy_status_mtproxy_no_server_hello",
|
|
"lng_proxy_status_mtproxy_no_appdata",
|
|
"lng_proxy_status_mtproxy_appdata_closed",
|
|
"lng_proxy_status_mtproxy_bad_response",
|
|
):
|
|
assert f'"{key}' in lang
|
|
|
|
|
|
if __name__ == "__main__":
|
|
for name, value in list(globals().items()):
|
|
if name.startswith("test_") and callable(value):
|
|
value()
|
|
print("ok")
|