Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions .github/workflows/benchmark.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,15 +112,24 @@ jobs:
path.write_text(text, encoding="utf-8")
print("Injected daemon.api_key into base ccbt.toml for benchmark compatibility")
PY
# Prefer en/ path; fall back to legacy docs/examples on older base trees.
# Resolve config relative to the *base* worktree only.
# Do not fall back to HEAD's docs/en path — that overwrote the legacy
# docs/examples path on older main and made hash_verify produce no JSON.
CONFIG_FILE="${BENCH_CONFIG_FILE}"
if [ ! -f "${BENCH_BASE_DIR}/${CONFIG_FILE}" ] && [ -f "${BENCH_BASE_DIR}/docs/examples/example-config-performance.toml" ]; then
CONFIG_FILE="docs/examples/example-config-performance.toml"
if [ ! -f "${BENCH_BASE_DIR}/${CONFIG_FILE}" ]; then
if [ -f "${BENCH_BASE_DIR}/docs/examples/example-config-performance.toml" ]; then
CONFIG_FILE="docs/examples/example-config-performance.toml"
elif [ -f "${BENCH_BASE_DIR}/docs/en/examples/example-config-performance.toml" ]; then
CONFIG_FILE="docs/en/examples/example-config-performance.toml"
fi
fi
if [ ! -f "${CONFIG_FILE}" ] && [ -f "docs/en/examples/example-config-performance.toml" ]; then
CONFIG_FILE="docs/en/examples/example-config-performance.toml"
if [ ! -f "${BENCH_BASE_DIR}/${CONFIG_FILE}" ]; then
echo "ERROR: No performance config found under ${BENCH_BASE_DIR} (tried ${CONFIG_FILE})"
exit 1
fi
echo "Using base benchmark config: ${CONFIG_FILE}"
(cd "${BENCH_BASE_DIR}" && uv sync --dev)
# Use HEAD's runner script (has current CLI), but execute benchmarks in base workdir.
uv run python dev/scripts/run_benchmark_suite.py \
--output-dir "${BENCH_BASE_DIR}" \
--workdir "${BENCH_BASE_DIR}" \
Expand Down
1 change: 1 addition & 0 deletions tests/unit/discovery/test_dht_bootstrap_readiness.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def _make_stub_client() -> AsyncDHTClient:
client._max_empty_table_rebootstrap_attempts = 3
client._last_empty_table_rebootstrap_at = 0.0
client._empty_table_rebootstrap_backoff = 1.0
client._empty_table_backoff_factor = 1.5
client._zero_node_rebootstrap_task = None
return client

Expand Down
2 changes: 1 addition & 1 deletion tests/unit/discovery/test_tracker_ssl.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ async def test_make_request_https_with_ssl_enabled(self):
response_data = await client._make_request_async(url)

assert response_data == b"response data"
mock_session.get.assert_called_once_with(url)
mock_session.get.assert_called_once_with(url, allow_redirects=False)

@pytest.mark.asyncio
async def test_make_request_https_with_ssl_disabled(self):
Expand Down
58 changes: 35 additions & 23 deletions tests/unit/security/test_mse_handshake.py
Original file line number Diff line number Diff line change
Expand Up @@ -1316,14 +1316,15 @@ async def test_receiver_resolves_candidate_hash_from_initial_payload() -> None:
chosen_info_hash = b"\x22" * 20
initial_payload = b"peer-handshake-placeholder"
responder_results: asyncio.Queue[MSEHandshakeResult] = asyncio.Queue()
handshake_timeout = _MSE_INTEGRATION_TIMEOUT

async def responder(reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None:
handshake = MSEHandshake()
result = await handshake.respond_as_receiver_with_initial_data(
reader=reader,
writer=writer,
info_hash=ignored_info_hash,
timeout=5.0,
timeout=handshake_timeout,
initial_payload_size=0,
info_hash_candidates=[ignored_info_hash, chosen_info_hash],
)
Expand All @@ -1334,29 +1335,40 @@ async def responder(reader: asyncio.StreamReader, writer: asyncio.StreamWriter)
server = await asyncio.start_server(responder, "127.0.0.1", 0)
try:
server_port = server.sockets[0].getsockname()[1]
initiator_reader, initiator_writer = await asyncio.open_connection(
"127.0.0.1", server_port
)
try:
initiator_handshake = MSEHandshake()
initiator_result = await initiator_handshake.initiate_as_initiator(
initiator_reader,
initiator_writer,
chosen_info_hash,
timeout=5.0,
initial_payload=initial_payload,
)
responder_result = await asyncio.wait_for(
responder_results.get(), timeout=5.0
last_error: str | None = None
initiator_result: MSEHandshakeResult | None = None
responder_result: MSEHandshakeResult | None = None
for attempt in range(2):
initiator_reader, initiator_writer = await asyncio.open_connection(
"127.0.0.1", server_port
)

assert initiator_result.success is True
assert responder_result.success is True
assert responder_result.resolved_info_hash == chosen_info_hash
assert responder_result.decrypted_initial_data == initial_payload
finally:
initiator_writer.close()
await initiator_writer.wait_closed()
try:
initiator_handshake = MSEHandshake()
initiator_result = await initiator_handshake.initiate_as_initiator(
initiator_reader,
initiator_writer,
chosen_info_hash,
timeout=handshake_timeout,
initial_payload=initial_payload,
)
responder_result = await asyncio.wait_for(
responder_results.get(), timeout=handshake_timeout
)
if initiator_result.success and responder_result.success:
break
last_error = initiator_result.error or responder_result.error
finally:
initiator_writer.close()
await initiator_writer.wait_closed()
if attempt == 0:
await asyncio.sleep(0.05)

assert initiator_result is not None
assert responder_result is not None
assert initiator_result.success is True, last_error
assert responder_result.success is True, last_error
assert responder_result.resolved_info_hash == chosen_info_hash
assert responder_result.decrypted_initial_data == initial_payload
finally:
server.close()
await server.wait_closed()
Expand Down
28 changes: 25 additions & 3 deletions tests/unit/session/test_peer_initializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from typing import Any

import ccbt.session.peers as peers_mod
from ccbt.config.config import get_config
from ccbt.session.models import SessionContext
from ccbt.session.peers import PeerManagerInitializer
Expand All @@ -12,16 +13,37 @@ def __init__(self, *_: Any, **__: Any) -> None:
self._started = False
self._security_manager = None
self._is_private = False
self.connections: dict[str, Any] = {}
self.on_peer_connected = None
self.on_peer_disconnected = None
self.on_piece_received = None
self.on_bitfield_received = None

def set_security_manager(self, _manager: Any) -> None:
self._security_manager = _manager

def set_is_private(self, is_private: bool) -> None:
self._is_private = is_private

async def start(self) -> None:
self._started = True

async def stop(self) -> None:
self._started = False

async def connect_to_peers(self, _peers: Any) -> None:
return None

def get_connected_peers(self) -> list[Any]:
return []

def get_active_peers(self) -> list[Any]:
return []


async def test_peer_initializer_binds_and_starts(monkeypatch: Any) -> None:
# Monkeypatch the async peer manager used inside the initializer
import ccbt.session.peers as peers_mod

peers_mod.AsyncPeerConnectionManager = FakePeerManager # type: ignore[attr-defined]
monkeypatch.setattr(peers_mod, "AsyncPeerConnectionManager", FakePeerManager)

class DM:
def __init__(self) -> None:
Expand Down
9 changes: 9 additions & 0 deletions tests/unit/session/test_session_manager_metrics_and_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ async def test_set_rate_limits_and_stats_aggregation(monkeypatch):
class _Dummy:
def __init__(self):
self.info = type("Info", (), {"status": "downloading"})()
self.download_manager = None
self.peer_manager = None
self._status = {
"status": "downloading",
"download_rate": 2.5,
Expand All @@ -24,6 +26,13 @@ def __init__(self):
"left": 900,
"peers": 3,
}
self._cached_status = {
"status": self._status["status"],
"download_rate": self._status["download_rate"],
"upload_rate": self._status["upload_rate"],
"progress": self._status["progress"],
"connected_peers": self._status["peers"],
}

async def get_status(self):
return dict(self._status)
Expand Down
6 changes: 6 additions & 0 deletions tests/unit/session/test_session_remaining_coverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,12 @@ async def test_aggregate_torrent_stats_with_torrents(monkeypatch):
class _Session:
def __init__(self, name):
self.name = name
self.download_manager = None
self.peer_manager = None
self._cached_status = {
"download_rate": 100.0,
"upload_rate": 50.0,
}

@property
def downloaded_bytes(self):
Expand Down
Loading