rename package netcheck to netsleuth (name taken on pypi)

This commit is contained in:
Andrew Pimenov 2026-08-09 11:43:00 +03:00
parent 0336be4080
commit 483989c1bc
45 changed files with 163 additions and 163 deletions

View file

@ -1,11 +1,11 @@
[project] [project]
name = "netcheck" name = "netsleuth"
version = "0.1.0" version = "0.1.0"
description = "Cross-platform CLI for deep network diagnostics: ISP/VPN/ASN identity, latency, path and bandwidth" description = "Cross-platform CLI for deep network diagnostics: ISP/VPN/ASN identity, latency, path and bandwidth"
readme = "README.md" readme = "README.md"
license = "PolyForm-Noncommercial-1.0.0" license = "PolyForm-Noncommercial-1.0.0"
requires-python = ">=3.10" requires-python = ">=3.10"
authors = [{ name = "netcheck contributors" }] authors = [{ name = "netsleuth contributors" }]
keywords = ["network", "diagnostics", "traceroute", "bgp", "vpn", "speedtest"] keywords = ["network", "diagnostics", "traceroute", "bgp", "vpn", "speedtest"]
dependencies = [ dependencies = [
"typer>=0.12", "typer>=0.12",
@ -24,7 +24,7 @@ tcptrace = ["scapy>=2.5"]
ndt7 = ["websockets>=12"] ndt7 = ["websockets>=12"]
[project.scripts] [project.scripts]
netcheck = "netcheck.cli:main" netsleuth = "netsleuth.cli:main"
[dependency-groups] [dependency-groups]
dev = [ dev = [
@ -38,9 +38,9 @@ requires = ["hatchling"]
build-backend = "hatchling.build" build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel] [tool.hatch.build.targets.wheel]
packages = ["src/netcheck"] packages = ["src/netsleuth"]
[tool.pytest.ini_options] [tool.pytest.ini_options]
testpaths = ["tests"] testpaths = ["tests"]
asyncio_mode = "auto" asyncio_mode = "auto"
filterwarnings = ["error::DeprecationWarning:netcheck.*"] filterwarnings = ["error::DeprecationWarning:netsleuth.*"]

View file

@ -18,7 +18,7 @@ colorama==0.4.6 ; sys_platform == 'win32'
# pytest # pytest
# typer # typer
dnspython==2.8.0 dnspython==2.8.0
# via netcheck # via netsleuth
exceptiongroup==1.3.1 ; python_full_version < '3.11' exceptiongroup==1.3.1 ; python_full_version < '3.11'
# via # via
# anyio # anyio
@ -33,12 +33,12 @@ httpcore==1.0.9
# via httpx # via httpx
httpx==0.28.1 httpx==0.28.1
# via # via
# netcheck # netsleuth
# pytest-httpx # pytest-httpx
hyperframe==6.1.0 hyperframe==6.1.0
# via h2 # via h2
icmplib==3.0.4 icmplib==3.0.4
# via netcheck # via netsleuth
idna==3.18 idna==3.18
# via # via
# anyio # anyio
@ -54,15 +54,15 @@ packaging==26.3
pluggy==1.6.0 pluggy==1.6.0
# via pytest # via pytest
psutil==7.2.2 psutil==7.2.2
# via netcheck # via netsleuth
pydantic==2.13.4 pydantic==2.13.4
# via # via
# netcheck # netsleuth
# pydantic-settings # pydantic-settings
pydantic-core==2.46.4 pydantic-core==2.46.4
# via pydantic # via pydantic
pydantic-settings==2.15.0 pydantic-settings==2.15.0
# via netcheck # via netsleuth
pygments==2.20.0 pygments==2.20.0
# via # via
# pytest # pytest
@ -76,17 +76,17 @@ pytest-httpx==0.36.2
python-dotenv==1.2.2 python-dotenv==1.2.2
# via pydantic-settings # via pydantic-settings
pyyaml==6.0.3 pyyaml==6.0.3
# via netcheck # via netsleuth
rich==15.0.0 rich==15.0.0
# via # via
# netcheck # netsleuth
# typer # typer
shellingham==1.5.4 shellingham==1.5.4
# via typer # via typer
tomli==2.4.1 ; python_full_version < '3.11' tomli==2.4.1 ; python_full_version < '3.11'
# via pytest # via pytest
typer==0.27.1 typer==0.27.1
# via netcheck # via netsleuth
typing-extensions==4.16.0 typing-extensions==4.16.0
# via # via
# anyio # anyio

View file

@ -1,4 +1,4 @@
from netcheck.cli import main from netsleuth.cli import main
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View file

@ -5,8 +5,8 @@ from datetime import datetime, timedelta, timezone
import httpx import httpx
from netcheck.config import Providers from netsleuth.config import Providers
from netcheck.models import BgpEvent from netsleuth.models import BgpEvent
# These calls return the whole routing history of an ASN; on a large ISP that is # These calls return the whole routing history of an ASN; on a large ISP that is
# tens of megabytes unless the window and row count are pinned. # tens of megabytes unless the window and row count are pinned.
@ -110,7 +110,7 @@ import time
from pathlib import Path from pathlib import Path
from typing import Awaitable, Callable from typing import Awaitable, Callable
from netcheck.models import IxpPresence from netsleuth.models import IxpPresence
def parse_asrank(payload: dict) -> tuple[int | None, int | None, int | None]: def parse_asrank(payload: dict) -> tuple[int | None, int | None, int | None]:
@ -174,7 +174,7 @@ async def cached_json(
return payload return payload
from netcheck.models import BgpIntel from netsleuth.models import BgpIntel
def build_bgp_intel( def build_bgp_intel(

View file

@ -13,25 +13,25 @@ import httpx
import typer import typer
from rich.console import Console from rich.console import Console
from netcheck import __version__ from netsleuth import __version__
from netcheck.compare import diff_reports, load_report, render_diff from netsleuth.compare import diff_reports, load_report, render_diff
from netcheck.config import Settings, load_settings from netsleuth.config import Settings, load_settings
from netcheck.exporter import build_report, egress_asn, render_markdown, report_filename, write_report from netsleuth.exporter import build_report, egress_asn, render_markdown, report_filename, write_report
from netcheck.interpret import ( from netsleuth.interpret import (
assess_vpn, assess_vpn,
gather_vpn_signals, gather_vpn_signals,
latency_findings, latency_findings,
path_findings, path_findings,
speed_findings, speed_findings,
) )
from netcheck.ip_geo import dual_stack_mismatch, gather_identity from netsleuth.ip_geo import dual_stack_mismatch, gather_identity
from netcheck.models import Finding, IpGeo, LocalNet, ModuleResult, SpeedResult from netsleuth.models import Finding, IpGeo, LocalNet, ModuleResult, SpeedResult
from netcheck.netinfo import collect_local_net, detect_capabilities, is_tunnel_iface from netsleuth.netinfo import collect_local_net, detect_capabilities, is_tunnel_iface
from netcheck.orchestration import gather_modules, run_module, utc_now_iso from netsleuth.orchestration import gather_modules, run_module, utc_now_iso
from netcheck.probes.dns_leak import collect_dns_leak from netsleuth.probes.dns_leak import collect_dns_leak
from netcheck.probes.latency import ping_fanout, tcp_connect_rtt from netsleuth.probes.latency import ping_fanout, tcp_connect_rtt
from netcheck.probes.traceroute import traceroute from netsleuth.probes.traceroute import traceroute
from netcheck.speed import NDT7_CONSENT_NOTICE from netsleuth.speed import NDT7_CONSENT_NOTICE
if sys.platform == "win32": if sys.platform == "win32":
# A legacy console codepage (e.g. cp1251) cannot encode the emoji badges and # A legacy console codepage (e.g. cp1251) cannot encode the emoji badges and
@ -150,7 +150,7 @@ async def _identity(client: httpx.AsyncClient, settings: Settings, options: Opti
async def _bgp_section(client, settings: Settings, asn: str | None, raw: dict): async def _bgp_section(client, settings: Settings, asn: str | None, raw: dict):
if not asn: if not asn:
return ModuleResult(name="bgp", status="skipped", warnings=["no ASN was resolved for this target"]) return ModuleResult(name="bgp", status="skipped", warnings=["no ASN was resolved for this target"])
from netcheck.bgp import collect_bgp from netsleuth.bgp import collect_bgp
key = settings.peeringdb_api_key.get_secret_value() if settings.peeringdb_api_key else None key = settings.peeringdb_api_key.get_secret_value() if settings.peeringdb_api_key else None
intel, payloads = await collect_bgp( intel, payloads = await collect_bgp(
@ -161,7 +161,7 @@ async def _bgp_section(client, settings: Settings, asn: str | None, raw: dict):
async def _reputation_section(client, settings: Settings, options: Options, geo: IpGeo, raw: dict): async def _reputation_section(client, settings: Settings, options: Options, geo: IpGeo, raw: dict):
from netcheck.reputation import ( from netsleuth.reputation import (
build_reputation, build_reputation,
fetch_abuseipdb, fetch_abuseipdb,
fetch_internetdb, fetch_internetdb,
@ -225,7 +225,7 @@ async def _traces(hosts, caps, settings: Settings, cycles: int, options: Options
async def _speed_section(client, settings: Settings, options: Options, idle_rtt_ms: float | None): async def _speed_section(client, settings: Settings, options: Options, idle_rtt_ms: float | None):
import shutil import shutil
from netcheck.speed import ( from netsleuth.speed import (
measure_with_bufferbloat, measure_with_bufferbloat,
run_speed_cascade, run_speed_cascade,
tier_cloudflare, tier_cloudflare,
@ -285,7 +285,7 @@ async def diagnose(settings: Settings, options: Options) -> tuple[dict, str, str
timeout=timeouts.http_seconds, timeout=timeouts.http_seconds,
follow_redirects=True, follow_redirects=True,
http2=True, http2=True,
headers={"User-Agent": f"netcheck/{__version__}"}, headers={"User-Agent": f"netsleuth/{__version__}"},
) as client: ) as client:
# Phase 1 — local facts and identity. Blocking: everything below needs the ASN. # Phase 1 — local facts and identity. Blocking: everything below needs the ASN.
modules["connection"] = await run_module( modules["connection"] = await run_module(
@ -463,12 +463,12 @@ def run(
"if it cannot run, the cascade falls through to the normal tiers.[/yellow]" "if it cannot run, the cascade falls through to the normal tiers.[/yellow]"
) )
if watch: if watch:
from netcheck.watch import run_watch from netsleuth.watch import run_watch
asyncio.run(run_watch(settings, options)) asyncio.run(run_watch(settings, options))
raise typer.Exit(0) raise typer.Exit(0)
console.print(f"netcheck {__version__} · {options.mode} mode · {platform.system()}") console.print(f"netsleuth {__version__} · {options.mode} mode · {platform.system()}")
report, _markdown, _markdown_ru, json_path, md_path, ru_md_path = asyncio.run(diagnose(settings, options)) report, _markdown, _markdown_ru, json_path, md_path, ru_md_path = asyncio.run(diagnose(settings, options))
interpretation = report["interpretation"] interpretation = report["interpretation"]
console.print( console.print(

View file

@ -5,7 +5,7 @@ from dataclasses import dataclass, field
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
from netcheck.exporter import badge from netsleuth.exporter import badge
@dataclass @dataclass
@ -124,7 +124,7 @@ def _block(title: str, changes: list[Change]) -> list[str]:
def render_diff(diff: ReportDiff, emoji: bool = True) -> str: def render_diff(diff: ReportDiff, emoji: bool = True) -> str:
lines = ["# netcheck compare", ""] lines = ["# netsleuth compare", ""]
lines += _block("Identity", diff.identity) lines += _block("Identity", diff.identity)
lines += _block("Latency", diff.latency) lines += _block("Latency", diff.latency)
lines += _block("Speed", diff.speed) lines += _block("Speed", diff.speed)

View file

@ -134,7 +134,7 @@ class Watch(BaseModel):
class Settings(BaseSettings): class Settings(BaseSettings):
model_config = SettingsConfigDict( model_config = SettingsConfigDict(
env_prefix="NETCHECK_", env_prefix="NETSLEUTH_",
env_nested_delimiter="__", env_nested_delimiter="__",
env_file_encoding="utf-8", env_file_encoding="utf-8",
extra="ignore", extra="ignore",

View file

@ -6,8 +6,8 @@ import re
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
from netcheck.interpret import overall_verdict from netsleuth.interpret import overall_verdict
from netcheck.models import Finding, ModuleResult, to_jsonable from netsleuth.models import Finding, ModuleResult, to_jsonable
SCHEMA_VERSION = 1 SCHEMA_VERSION = 1
SECTION_ORDER = ( SECTION_ORDER = (
@ -101,7 +101,7 @@ def write_report(
return json_path, md_path, ru_md_path return json_path, md_path, ru_md_path
from netcheck.interpret import bufferbloat_consequence from netsleuth.interpret import bufferbloat_consequence
SPARK_CHARS = "▁▂▃▅▇" SPARK_CHARS = "▁▂▃▅▇"
_BADGES = {"ok": "🟢", "info": "🟢", "warn": "🟡", "crit": "🔴"} _BADGES = {"ok": "🟢", "info": "🟢", "warn": "🟡", "crit": "🔴"}
@ -210,7 +210,7 @@ def _header(report: dict, emoji: bool, lang: str = "en") -> list[str]:
summary = interp.get("summary_text", "") if lang == "en" else _ru_summary(report, status) summary = interp.get("summary_text", "") if lang == "en" else _ru_summary(report, status)
status_word = status if lang == "en" else _SEVERITY_WORDS_RU.get(status, status) status_word = status if lang == "en" else _SEVERITY_WORDS_RU.get(status, status)
return [ return [
"# netcheck report", "# netsleuth report",
"", "",
f"- {_t(lang, 'Mode', 'Режим')}: `{meta.get('mode', 'auto')}`{target}", f"- {_t(lang, 'Mode', 'Режим')}: `{meta.get('mode', 'auto')}`{target}",
f"- {_t(lang, 'Started', 'Начало')}: {meta.get('started_at', '?')} · " f"- {_t(lang, 'Started', 'Начало')}: {meta.get('started_at', '?')} · "

View file

@ -2,8 +2,8 @@ from __future__ import annotations
from typing import Iterable from typing import Iterable
from netcheck.config import Band, BufferbloatBands, Thresholds, VpnBands from netsleuth.config import Band, BufferbloatBands, Thresholds, VpnBands
from netcheck.models import ( from netsleuth.models import (
CfTrace, CfTrace,
DnsLeak, DnsLeak,
Finding, Finding,
@ -15,7 +15,7 @@ from netcheck.models import (
TraceResult, TraceResult,
VpnAssessment, VpnAssessment,
) )
from netcheck.netinfo import is_tunnel_iface, mtu_anomaly from netsleuth.netinfo import is_tunnel_iface, mtu_anomaly
_SEVERITY_ORDER = {"ok": 0, "info": 1, "warn": 2, "crit": 3} _SEVERITY_ORDER = {"ok": 0, "info": 1, "warn": 2, "crit": 3}

View file

@ -3,7 +3,7 @@ from __future__ import annotations
import ipaddress import ipaddress
import re import re
from netcheck.models import CfTrace, IpGeo from netsleuth.models import CfTrace, IpGeo
_AS_PREFIX_RE = re.compile(r"^AS(\d+)\s*(?P<name>.*)$", re.IGNORECASE) _AS_PREFIX_RE = re.compile(r"^AS(\d+)\s*(?P<name>.*)$", re.IGNORECASE)
@ -214,7 +214,7 @@ import asyncio
import httpx import httpx
from netcheck.config import Providers from netsleuth.config import Providers
async def _json(client: httpx.AsyncClient, url: str, **kwargs) -> dict: async def _json(client: httpx.AsyncClient, url: str, **kwargs) -> dict:

View file

@ -12,7 +12,7 @@ import subprocess
import psutil import psutil
from netcheck.models import Capabilities, LocalNet from netsleuth.models import Capabilities, LocalNet
_TUNNEL_PATTERNS = ( _TUNNEL_PATTERNS = (
re.compile(r"^(tun|tap|utun|ppp|wg|nordlynx|proton|ipsec|gpd)\d*", re.IGNORECASE), re.compile(r"^(tun|tap|utun|ppp|wg|nordlynx|proton|ipsec|gpd)\d*", re.IGNORECASE),

View file

@ -7,7 +7,7 @@ from typing import Any, Awaitable
import httpx import httpx
from netcheck.models import ModuleResult, ProbeError from netsleuth.models import ModuleResult, ProbeError
_BLOCKED_STATUS = {401, 403, 451} _BLOCKED_STATUS = {401, 403, 451}
_RETRYABLE_STATUS_FLOOR = 500 _RETRYABLE_STATUS_FLOOR = 500

View file

@ -2,7 +2,7 @@ from __future__ import annotations
import ipaddress import ipaddress
from netcheck.models import AdapterLeakResult, DnsLeak from netsleuth.models import AdapterLeakResult, DnsLeak
_LEAK_NOTE = ( _LEAK_NOTE = (
"This test only sees resolvers configured at the OS level. A browser using DoH " "This test only sees resolvers configured at the OS level. A browser using DoH "
@ -99,8 +99,8 @@ import asyncio
import dns.asyncresolver import dns.asyncresolver
import dns.exception import dns.exception
from netcheck.bgp import parse_cymru_origin from netsleuth.bgp import parse_cymru_origin
from netcheck.models import LocalNet from netsleuth.models import LocalNet
MYADDR_NAME = "o-o.myaddr.l.google.com" MYADDR_NAME = "o-o.myaddr.l.google.com"
AKAHELP_NAME = "whoami.ds.akahelp.net" AKAHELP_NAME = "whoami.ds.akahelp.net"

View file

@ -107,7 +107,7 @@ def _handle():
return iphlpapi, handle return iphlpapi, handle
def echo_once(dest: str, ttl: int, timeout_ms: int, payload: bytes = b"netcheck") -> IcmpReply: def echo_once(dest: str, ttl: int, timeout_ms: int, payload: bytes = b"netsleuth") -> IcmpReply:
iphlpapi, handle = _handle() iphlpapi, handle = _handle()
try: try:
options = _IpOptionInformation(Ttl=ttl, Tos=0, Flags=0, OptionsSize=0, OptionsData=None) options = _IpOptionInformation(Ttl=ttl, Tos=0, Flags=0, OptionsSize=0, OptionsData=None)

View file

@ -4,9 +4,9 @@ import asyncio
import socket import socket
import time import time
from netcheck.models import Capabilities, PingResult from netsleuth.models import Capabilities, PingResult
from netcheck.netinfo import choose_latency_backend from netsleuth.netinfo import choose_latency_backend
from netcheck.stats import rtt_stats from netsleuth.stats import rtt_stats
def summarize_ping( def summarize_ping(
@ -57,7 +57,7 @@ def _resolve(host: str) -> str | None:
async def _icmp_samples(host: str, count: int, interval: float, timeout: float, backend: str) -> list[float | None]: async def _icmp_samples(host: str, count: int, interval: float, timeout: float, backend: str) -> list[float | None]:
if backend == "icmp_win": if backend == "icmp_win":
from netcheck.probes.icmp_win import ping_samples_win from netsleuth.probes.icmp_win import ping_samples_win
return await asyncio.to_thread(ping_samples_win, host, count, interval, timeout) return await asyncio.to_thread(ping_samples_win, host, count, interval, timeout)
from icmplib import async_ping from icmplib import async_ping

View file

@ -8,9 +8,9 @@ import shutil
import threading import threading
from typing import Awaitable, Callable, TypeVar from typing import Awaitable, Callable, TypeVar
from netcheck.models import Capabilities, TraceHop, TraceResult from netsleuth.models import Capabilities, TraceHop, TraceResult
from netcheck.probes.icmp_win import IcmpReply, classify_status from netsleuth.probes.icmp_win import IcmpReply, classify_status
from netcheck.traceparse import build_trace_result, finalize_hop from netsleuth.traceparse import build_trace_result, finalize_hop
_T = TypeVar("_T") _T = TypeVar("_T")
@ -34,7 +34,7 @@ def _run_in_daemon_thread(func: Callable[..., _T], *args: object) -> "asyncio.Fu
else: else:
result.set_result(value) result.set_result(value)
threading.Thread(target=_target, name="netcheck-icmp-win", daemon=True).start() threading.Thread(target=_target, name="netsleuth-icmp-win", daemon=True).start()
return asyncio.wrap_future(result) return asyncio.wrap_future(result)
@ -129,7 +129,7 @@ async def _tier_mtr(target: str, binary: str, cycles: int, max_hops: int, timeou
async def _tier_icmp_win(target: str, max_hops: int, timeout: float) -> TraceResult: async def _tier_icmp_win(target: str, max_hops: int, timeout: float) -> TraceResult:
from netcheck.probes.icmp_win import trace_hops_win from netsleuth.probes.icmp_win import trace_hops_win
replies = await _run_in_daemon_thread(trace_hops_win, target, max_hops, int(timeout * 1000)) replies = await _run_in_daemon_thread(trace_hops_win, target, max_hops, int(timeout * 1000))
hops = hops_from_win_replies(replies) hops = hops_from_win_replies(replies)

View file

@ -12,8 +12,8 @@ import dns.asyncresolver
import dns.exception import dns.exception
import httpx import httpx
from netcheck.config import Providers from netsleuth.config import Providers
from netcheck.models import DnsblHit, InternetDbResult, Reputation from netsleuth.models import DnsblHit, InternetDbResult, Reputation
def parse_netset(text: str) -> list[str]: def parse_netset(text: str) -> list[str]:

View file

@ -3,8 +3,8 @@ from __future__ import annotations
import re import re
from urllib.parse import parse_qs from urllib.parse import parse_qs
from netcheck.models import CfL4Stats from netsleuth.models import CfL4Stats
from netcheck.stats import percentile from netsleuth.stats import percentile
_CFL4_RE = re.compile(r'cfL4\s*;\s*desc\s*=\s*"?\??(?P<query>[^",]*)"?') _CFL4_RE = re.compile(r'cfL4\s*;\s*desc\s*=\s*"?\??(?P<query>[^",]*)"?')
@ -62,7 +62,7 @@ import asyncio
import time import time
from typing import Awaitable, Callable from typing import Awaitable, Callable
from netcheck.models import SpeedResult, TierAttempt from netsleuth.models import SpeedResult, TierAttempt
NDT7_CONSENT_NOTICE = ( NDT7_CONSENT_NOTICE = (
"M-Lab NDT7 publishes every measurement as public CC0 open data, including your " "M-Lab NDT7 publishes every measurement as public CC0 open data, including your "
@ -106,7 +106,7 @@ import json
import httpx import httpx
from netcheck.config import Speedtest from netsleuth.config import Speedtest
async def tier_ookla(binary: str, server: str | None, timeout: float) -> SpeedResult: async def tier_ookla(binary: str, server: str | None, timeout: float) -> SpeedResult:
@ -213,9 +213,9 @@ async def tier_ndt7(client: httpx.AsyncClient, cfg: Speedtest, timeout: float) -
) )
from netcheck.config import BufferbloatBands from netsleuth.config import BufferbloatBands
from netcheck.interpret import grade_bufferbloat from netsleuth.interpret import grade_bufferbloat
from netcheck.stats import rtt_stats from netsleuth.stats import rtt_stats
async def probe_while( async def probe_while(

View file

@ -2,8 +2,8 @@ from __future__ import annotations
import re import re
from netcheck.models import TraceHop, TraceResult from netsleuth.models import TraceHop, TraceResult
from netcheck.stats import rtt_stats from netsleuth.stats import rtt_stats
IPV4_RE = re.compile(r"\b(?:\d{1,3}\.){3}\d{1,3}\b") IPV4_RE = re.compile(r"\b(?:\d{1,3}\.){3}\d{1,3}\b")
IPV6_RE = re.compile(r"\b(?:[0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}\b") IPV6_RE = re.compile(r"\b(?:[0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}\b")

View file

@ -11,14 +11,14 @@ from rich.console import Console
from rich.live import Live from rich.live import Live
from rich.table import Table from rich.table import Table
from netcheck import __version__ from netsleuth import __version__
from netcheck.config import Settings from netsleuth.config import Settings
from netcheck.exporter import atomic_write, compact_timestamp, dump_json, sanitize_name, sparkline from netsleuth.exporter import atomic_write, compact_timestamp, dump_json, sanitize_name, sparkline
from netcheck.ip_geo import gather_identity from netsleuth.ip_geo import gather_identity
from netcheck.models import PingResult, SpeedResult, to_jsonable from netsleuth.models import PingResult, SpeedResult, to_jsonable
from netcheck.netinfo import detect_capabilities from netsleuth.netinfo import detect_capabilities
from netcheck.orchestration import run_module, utc_now_iso from netsleuth.orchestration import run_module, utc_now_iso
from netcheck.probes.latency import ping_fanout from netsleuth.probes.latency import ping_fanout
WATCH_SCHEMA_VERSION = 1 WATCH_SCHEMA_VERSION = 1
@ -112,7 +112,7 @@ def write_session(session: WatchSession, logs_dir: Path) -> Path:
def render_dashboard(session: WatchSession) -> Table: def render_dashboard(session: WatchSession) -> Table:
table = Table(title=f"netcheck --watch · {session.asn or 'unknown ASN'} · cycle {len(session.cycles)}") table = Table(title=f"netsleuth --watch · {session.asn or 'unknown ASN'} · cycle {len(session.cycles)}")
for column in ("Host", "Avg ms", "Jitter", "Loss", "Trend"): for column in ("Host", "Avg ms", "Jitter", "Loss", "Trend"):
table.add_column(column) table.add_column(column)
latest = session.cycles[-1]["hosts"] if session.cycles else {} latest = session.cycles[-1]["hosts"] if session.cycles else {}
@ -137,7 +137,7 @@ def render_dashboard(session: WatchSession) -> Table:
async def run_watch(settings: Settings, options) -> Path: async def run_watch(settings: Settings, options) -> Path:
# Imported here, not at module import time: cli imports watch lazily for --watch, # Imported here, not at module import time: cli imports watch lazily for --watch,
# and a module-level import back into cli would close the cycle. # and a module-level import back into cli would close the cycle.
from netcheck.cli import _speed_section from netsleuth.cli import _speed_section
caps = detect_capabilities() caps = detect_capabilities()
session = WatchSession( session = WatchSession(
@ -149,12 +149,12 @@ async def run_watch(settings: Settings, options) -> Path:
if options.extra_host: if options.extra_host:
hosts.append(("target-host", options.extra_host)) hosts.append(("target-host", options.extra_host))
console = Console() console = Console()
console.print(f"netcheck {__version__} · watching every {session.interval_seconds}s · Ctrl-C to stop") console.print(f"netsleuth {__version__} · watching every {session.interval_seconds}s · Ctrl-C to stop")
async with httpx.AsyncClient( async with httpx.AsyncClient(
timeout=settings.timeouts.http_seconds, timeout=settings.timeouts.http_seconds,
follow_redirects=True, follow_redirects=True,
headers={"User-Agent": f"netcheck/{__version__}"}, headers={"User-Agent": f"netsleuth/{__version__}"},
) as client: ) as client:
try: try:
geo, _cf, _flags, _raw = await gather_identity(client, settings.providers) geo, _cf, _flags, _raw = await gather_identity(client, settings.providers)

View file

@ -3,7 +3,7 @@ from __future__ import annotations
import httpx import httpx
import pytest import pytest
from netcheck.bgp import ( from netsleuth.bgp import (
classify_stability, classify_stability,
parse_announced_prefixes, parse_announced_prefixes,
parse_as_overview, parse_as_overview,
@ -11,8 +11,8 @@ from netcheck.bgp import (
parse_bgp_updates, parse_bgp_updates,
ripestat, ripestat,
) )
from netcheck.config import Providers from netsleuth.config import Providers
from netcheck.models import BgpEvent from netsleuth.models import BgpEvent
def test_as_overview_extracts_holder_and_block(api_fixture): def test_as_overview_extracts_holder_and_block(api_fixture):
@ -99,7 +99,7 @@ async def test_ripestat_does_not_bound_non_bulk_calls(httpx_mock):
import json import json
from pathlib import Path from pathlib import Path
from netcheck.bgp import ( from netsleuth.bgp import (
cached_json, cached_json,
parse_asrank, parse_asrank,
parse_cymru_origin, parse_cymru_origin,
@ -211,7 +211,7 @@ async def test_cached_json_survives_a_corrupt_cache_file(tmp_path: Path):
assert await cached_json(tmp_path, "k", ttl_hours=24, fetch=fetch) == {"value": "fresh"} assert await cached_json(tmp_path, "k", ttl_hours=24, fetch=fetch) == {"value": "fresh"}
from netcheck.bgp import build_bgp_intel from netsleuth.bgp import build_bgp_intel
def test_build_bgp_intel_merges_every_source(api_fixture): def test_build_bgp_intel_merges_every_source(api_fixture):

View file

@ -2,7 +2,7 @@ from __future__ import annotations
import pytest import pytest
from netcheck.compare import ( from netsleuth.compare import (
Change, Change,
diff_reports, diff_reports,
finding_changes, finding_changes,
@ -101,7 +101,7 @@ def test_diff_reports_assembles_every_part(before, after):
def test_render_diff_shows_before_and_after_values(before, after): def test_render_diff_shows_before_and_after_values(before, after):
text = render_diff(diff_reports(before, after)) text = render_diff(diff_reports(before, after))
assert text.startswith("# netcheck compare") assert text.startswith("# netsleuth compare")
assert "203.0.113.44" in text assert "203.0.113.44" in text
assert "198.51.100.7" in text assert "198.51.100.7" in text
assert "-188.2" in text assert "-188.2" in text

View file

@ -4,7 +4,7 @@ import textwrap
import pytest import pytest
from netcheck.config import load_settings from netsleuth.config import load_settings
@pytest.fixture() @pytest.fixture()
@ -48,15 +48,15 @@ def test_yaml_overrides_defaults(yaml_file, tmp_path):
def test_dotenv_overrides_yaml(yaml_file, tmp_path): def test_dotenv_overrides_yaml(yaml_file, tmp_path):
env_file = tmp_path / ".env" env_file = tmp_path / ".env"
env_file.write_text("NETCHECK_PROBING__PING_COUNT=13\n", encoding="utf-8") env_file.write_text("NETSLEUTH_PROBING__PING_COUNT=13\n", encoding="utf-8")
s = load_settings(config_path=yaml_file, env_file=env_file) s = load_settings(config_path=yaml_file, env_file=env_file)
assert s.probing.ping_count == 13 assert s.probing.ping_count == 13
def test_environment_overrides_dotenv(yaml_file, tmp_path, monkeypatch): def test_environment_overrides_dotenv(yaml_file, tmp_path, monkeypatch):
env_file = tmp_path / ".env" env_file = tmp_path / ".env"
env_file.write_text("NETCHECK_PROBING__PING_COUNT=13\n", encoding="utf-8") env_file.write_text("NETSLEUTH_PROBING__PING_COUNT=13\n", encoding="utf-8")
monkeypatch.setenv("NETCHECK_PROBING__PING_COUNT", "99") monkeypatch.setenv("NETSLEUTH_PROBING__PING_COUNT", "99")
s = load_settings(config_path=yaml_file, env_file=env_file) s = load_settings(config_path=yaml_file, env_file=env_file)
assert s.probing.ping_count == 99 assert s.probing.ping_count == 99

View file

@ -1,6 +1,6 @@
from __future__ import annotations from __future__ import annotations
from netcheck.probes.dns_leak import ( from netsleuth.probes.dns_leak import (
build_adapter_result, build_adapter_result,
build_dns_leak, build_dns_leak,
detect_ecs_leak, detect_ecs_leak,

View file

@ -2,7 +2,7 @@ from __future__ import annotations
import pytest import pytest
from netcheck.reputation import decode_dnsbl, reverse_ip, summarize_dnsbl from netsleuth.reputation import decode_dnsbl, reverse_ip, summarize_dnsbl
def test_reverse_ip_builds_the_dnsbl_query_label(): def test_reverse_ip_builds_the_dnsbl_query_label():

View file

@ -6,7 +6,7 @@ from pathlib import Path
import pytest import pytest
from netcheck.exporter import ( from netsleuth.exporter import (
SCHEMA_VERSION, SCHEMA_VERSION,
SECTION_ORDER, SECTION_ORDER,
atomic_write, atomic_write,
@ -19,7 +19,7 @@ from netcheck.exporter import (
sanitize_name, sanitize_name,
write_report, write_report,
) )
from netcheck.models import ( from netsleuth.models import (
Finding, Finding,
IpGeo, IpGeo,
LocalNet, LocalNet,
@ -196,7 +196,7 @@ def test_egress_asn_is_read_out_of_the_assembled_report():
def test_write_report_names_the_file_after_the_target_in_target_mode(tmp_path: Path): def test_write_report_names_the_file_after_the_target_in_target_mode(tmp_path: Path):
report = build_report({**meta(), "mode": "target", "target": "AS15169"}, modules(), [], {}) report = build_report({**meta(), "mode": "target", "target": "AS15169"}, modules(), [], {})
json_path, md_path, ru_md_path = write_report( json_path, md_path, ru_md_path = write_report(
report, "# netcheck report\n", "# netcheck report\n", tmp_path report, "# netsleuth report\n", "# netsleuth report\n", tmp_path
) )
assert json_path.name == "report_AS15169_20260808T191200Z.json" assert json_path.name == "report_AS15169_20260808T191200Z.json"
assert md_path.name == "report_AS15169_20260808T191200Z.md" assert md_path.name == "report_AS15169_20260808T191200Z.md"
@ -221,18 +221,18 @@ def test_atomic_write_replaces_an_existing_file(tmp_path: Path):
def test_write_report_emits_both_artifacts_with_matching_names(tmp_path: Path): def test_write_report_emits_both_artifacts_with_matching_names(tmp_path: Path):
report = build_report(meta(), modules(), [], {}) report = build_report(meta(), modules(), [], {})
json_path, md_path, ru_md_path = write_report( json_path, md_path, ru_md_path = write_report(
report, "# netcheck report\n", "# netcheck report\n", tmp_path report, "# netsleuth report\n", "# netsleuth report\n", tmp_path
) )
assert json_path.name == "report_AS64500_20260808T191200Z.json" assert json_path.name == "report_AS64500_20260808T191200Z.json"
assert md_path.name == "report_AS64500_20260808T191200Z.md" assert md_path.name == "report_AS64500_20260808T191200Z.md"
assert ru_md_path.name == "report_AS64500_20260808T191200Z.ru.md" assert ru_md_path.name == "report_AS64500_20260808T191200Z.ru.md"
assert json.loads(json_path.read_text(encoding="utf-8"))["schema_version"] == SCHEMA_VERSION assert json.loads(json_path.read_text(encoding="utf-8"))["schema_version"] == SCHEMA_VERSION
assert md_path.read_text(encoding="utf-8").startswith("# netcheck report") assert md_path.read_text(encoding="utf-8").startswith("# netsleuth report")
assert ru_md_path.read_text(encoding="utf-8").startswith("# netcheck report") assert ru_md_path.read_text(encoding="utf-8").startswith("# netsleuth report")
from netcheck.exporter import SPARK_CHARS, badge, first_loss_jump, render_markdown, sparkline from netsleuth.exporter import SPARK_CHARS, badge, first_loss_jump, render_markdown, sparkline
from netcheck.models import ( from netsleuth.models import (
AdapterLeakResult, AdapterLeakResult,
BgpIntel, BgpIntel,
CfTrace, CfTrace,
@ -466,7 +466,7 @@ def test_markdown_has_every_section_from_the_spec_in_order():
"## Problems & recommendations", "## Problems & recommendations",
"## Run diagnostics", "## Run diagnostics",
] ]
assert text.startswith("# netcheck report") assert text.startswith("# netsleuth report")
def test_markdown_header_states_mode_timestamp_and_verdict(): def test_markdown_header_states_mode_timestamp_and_verdict():
@ -542,7 +542,7 @@ def test_markdown_without_emoji_uses_text_badges():
assert "🟡" not in text assert "🟡" not in text
from netcheck.exporter import unavailable from netsleuth.exporter import unavailable
def dead_modules() -> dict[str, ModuleResult]: def dead_modules() -> dict[str, ModuleResult]:
@ -603,7 +603,7 @@ def test_unavailable_translates_the_skip_reason_into_russian():
def test_diagnostics_translates_module_warnings_into_russian(): def test_diagnostics_translates_module_warnings_into_russian():
from netcheck.exporter import _diagnostics from netsleuth.exporter import _diagnostics
report = build_report( report = build_report(
meta(), meta(),
@ -666,7 +666,7 @@ def test_russian_markdown_is_non_empty_and_contains_cyrillic():
text = render_markdown(full_report(), lang="ru") text = render_markdown(full_report(), lang="ru")
assert text.strip() assert text.strip()
assert _has_cyrillic(text) assert _has_cyrillic(text)
assert text.startswith("# netcheck report") assert text.startswith("# netsleuth report")
def _structural_shape(text: str) -> tuple[int, int, int]: def _structural_shape(text: str) -> tuple[int, int, int]:
@ -723,7 +723,7 @@ def test_render_markdown_default_call_is_byte_identical_to_before_the_lang_featu
"## Problems & recommendations", "## Problems & recommendations",
"## Run diagnostics", "## Run diagnostics",
] ]
assert text.startswith("# netcheck report") assert text.startswith("# netsleuth report")
assert "[Русский]" not in text assert "[Русский]" not in text
assert "[English]" not in text assert "[English]" not in text
assert "cloudflare-dns" in text assert "cloudflare-dns" in text

View file

@ -4,7 +4,7 @@ import struct
import pytest import pytest
from netcheck.probes.icmp_win import ( from netsleuth.probes.icmp_win import (
IP_DEST_HOST_UNREACHABLE, IP_DEST_HOST_UNREACHABLE,
IP_REQ_TIMED_OUT, IP_REQ_TIMED_OUT,
IP_SUCCESS, IP_SUCCESS,

View file

@ -3,7 +3,7 @@ from __future__ import annotations
import time import time
from unittest.mock import patch from unittest.mock import patch
from netcheck.probes.icmp_win import IP_REQ_TIMED_OUT, IcmpReply, trace_hops_win from netsleuth.probes.icmp_win import IP_REQ_TIMED_OUT, IcmpReply, trace_hops_win
def _fake_gethostbyname(host: str) -> str: def _fake_gethostbyname(host: str) -> str:
@ -19,14 +19,14 @@ def test_trace_hops_win_total_elapsed_is_bounded_by_the_total_budget_not_hops_ti
# to the 100ms total budget. # to the 100ms total budget.
call_timeouts: list[int] = [] call_timeouts: list[int] = []
def fake_echo_once(dest: str, ttl: int, timeout_ms: int, payload: bytes = b"netcheck") -> IcmpReply: def fake_echo_once(dest: str, ttl: int, timeout_ms: int, payload: bytes = b"netsleuth") -> IcmpReply:
call_timeouts.append(timeout_ms) call_timeouts.append(timeout_ms)
time.sleep(timeout_ms / 1000.0) time.sleep(timeout_ms / 1000.0)
return IcmpReply(address=None, status=IP_REQ_TIMED_OUT, rtt_ms=None, ttl=0) return IcmpReply(address=None, status=IP_REQ_TIMED_OUT, rtt_ms=None, ttl=0)
with ( with (
patch("netcheck.probes.icmp_win.socket.gethostbyname", side_effect=_fake_gethostbyname), patch("netsleuth.probes.icmp_win.socket.gethostbyname", side_effect=_fake_gethostbyname),
patch("netcheck.probes.icmp_win.echo_once", side_effect=fake_echo_once), patch("netsleuth.probes.icmp_win.echo_once", side_effect=fake_echo_once),
): ):
started = time.monotonic() started = time.monotonic()
trace_hops_win("example.invalid", max_hops=30, timeout_ms=100) trace_hops_win("example.invalid", max_hops=30, timeout_ms=100)
@ -43,13 +43,13 @@ def test_trace_hops_win_total_elapsed_is_bounded_by_the_total_budget_not_hops_ti
def test_trace_hops_win_per_hop_timeout_is_capped_even_for_a_huge_total_budget(): def test_trace_hops_win_per_hop_timeout_is_capped_even_for_a_huge_total_budget():
call_timeouts: list[int] = [] call_timeouts: list[int] = []
def fake_echo_once(dest: str, ttl: int, timeout_ms: int, payload: bytes = b"netcheck") -> IcmpReply: def fake_echo_once(dest: str, ttl: int, timeout_ms: int, payload: bytes = b"netsleuth") -> IcmpReply:
call_timeouts.append(timeout_ms) call_timeouts.append(timeout_ms)
return IcmpReply(address=None, status=IP_REQ_TIMED_OUT, rtt_ms=None, ttl=0) return IcmpReply(address=None, status=IP_REQ_TIMED_OUT, rtt_ms=None, ttl=0)
with ( with (
patch("netcheck.probes.icmp_win.socket.gethostbyname", side_effect=_fake_gethostbyname), patch("netsleuth.probes.icmp_win.socket.gethostbyname", side_effect=_fake_gethostbyname),
patch("netcheck.probes.icmp_win.echo_once", side_effect=fake_echo_once), patch("netsleuth.probes.icmp_win.echo_once", side_effect=fake_echo_once),
): ):
# A single-hop trace with a huge total budget (e.g. subprocess_seconds=60s # A single-hop trace with a huge total budget (e.g. subprocess_seconds=60s
# divided across a small max_hops) must still not hand the whole budget # divided across a small max_hops) must still not hand the whole budget
@ -60,16 +60,16 @@ def test_trace_hops_win_per_hop_timeout_is_capped_even_for_a_huge_total_budget()
def test_trace_hops_win_happy_path_hop_count_and_ok_break_are_unchanged(): def test_trace_hops_win_happy_path_hop_count_and_ok_break_are_unchanged():
from netcheck.probes.icmp_win import IP_SUCCESS from netsleuth.probes.icmp_win import IP_SUCCESS
def fake_echo_once(dest: str, ttl: int, timeout_ms: int, payload: bytes = b"netcheck") -> IcmpReply: def fake_echo_once(dest: str, ttl: int, timeout_ms: int, payload: bytes = b"netsleuth") -> IcmpReply:
if ttl < 3: if ttl < 3:
return IcmpReply(address=f"10.0.0.{ttl}", status=IP_REQ_TIMED_OUT, rtt_ms=None, ttl=0) return IcmpReply(address=f"10.0.0.{ttl}", status=IP_REQ_TIMED_OUT, rtt_ms=None, ttl=0)
return IcmpReply(address="203.0.113.1", status=IP_SUCCESS, rtt_ms=5.0, ttl=64) return IcmpReply(address="203.0.113.1", status=IP_SUCCESS, rtt_ms=5.0, ttl=64)
with ( with (
patch("netcheck.probes.icmp_win.socket.gethostbyname", side_effect=_fake_gethostbyname), patch("netsleuth.probes.icmp_win.socket.gethostbyname", side_effect=_fake_gethostbyname),
patch("netcheck.probes.icmp_win.echo_once", side_effect=fake_echo_once), patch("netsleuth.probes.icmp_win.echo_once", side_effect=fake_echo_once),
): ):
hops = trace_hops_win("example.invalid", max_hops=30, timeout_ms=60_000) hops = trace_hops_win("example.invalid", max_hops=30, timeout_ms=60_000)

View file

@ -2,9 +2,9 @@ from __future__ import annotations
import pytest import pytest
from netcheck.config import Band, Thresholds from netsleuth.config import Band, Thresholds
from netcheck.models import PingResult, TraceHop, TraceResult from netsleuth.models import PingResult, TraceHop, TraceResult
from netcheck.interpret import latency_findings, path_findings, severity_for, worst from netsleuth.interpret import latency_findings, path_findings, severity_for, worst
@pytest.fixture() @pytest.fixture()
@ -156,9 +156,9 @@ def test_path_findings_report_an_incomplete_trace():
assert [f.id for f in path_findings(trace)] == ["path.incomplete"] assert [f.id for f in path_findings(trace)] == ["path.incomplete"]
from netcheck.config import VpnBands from netsleuth.config import VpnBands
from netcheck.models import AdapterLeakResult, CfTrace, DnsLeak, IpGeo, LocalNet, Signal from netsleuth.models import AdapterLeakResult, CfTrace, DnsLeak, IpGeo, LocalNet, Signal
from netcheck.interpret import SIGNAL_WEIGHTS, assess_vpn, gather_vpn_signals, score_vpn from netsleuth.interpret import SIGNAL_WEIGHTS, assess_vpn, gather_vpn_signals, score_vpn
def sig(name: str, observed: bool = True) -> Signal: def sig(name: str, observed: bool = True) -> Signal:
@ -303,9 +303,9 @@ def test_assess_vpn_returns_a_complete_assessment():
assert assessment.confidence >= 0.75 assert assessment.confidence >= 0.75
from netcheck.config import BufferbloatBands from netsleuth.config import BufferbloatBands
from netcheck.models import Finding, SpeedResult from netsleuth.models import Finding, SpeedResult
from netcheck.interpret import bufferbloat_consequence, grade_bufferbloat, overall_verdict, speed_findings from netsleuth.interpret import bufferbloat_consequence, grade_bufferbloat, overall_verdict, speed_findings
def test_bufferbloat_grades_follow_the_configured_bands(): def test_bufferbloat_grades_follow_the_configured_bands():

View file

@ -3,8 +3,8 @@ from __future__ import annotations
import httpx import httpx
import pytest import pytest
from netcheck.config import Providers from netsleuth.config import Providers
from netcheck.ip_geo import ( from netsleuth.ip_geo import (
classify_ip_type, classify_ip_type,
gather_identity, gather_identity,
normalize_freeipapi, normalize_freeipapi,
@ -130,8 +130,8 @@ def test_ip_type_classification(mobile, proxy, hosting, known, expected):
assert classify_ip_type(mobile, proxy, hosting, known) == expected assert classify_ip_type(mobile, proxy, hosting, known) == expected
from netcheck.models import IpGeo from netsleuth.models import IpGeo
from netcheck.ip_geo import dual_stack_mismatch, merge_geo from netsleuth.ip_geo import dual_stack_mismatch, merge_geo
def test_merge_takes_the_first_non_empty_value_in_priority_order(): def test_merge_takes_the_first_non_empty_value_in_priority_order():

View file

@ -4,7 +4,7 @@ import json
import pytest import pytest
from netcheck.models import ( from netsleuth.models import (
AdapterLeakResult, AdapterLeakResult,
BgpEvent, BgpEvent,
BgpIntel, BgpIntel,

View file

@ -1,7 +1,7 @@
from __future__ import annotations from __future__ import annotations
from netcheck.models import Capabilities from netsleuth.models import Capabilities
from netcheck.netinfo import choose_latency_backend, choose_trace_backend, degradation_note from netsleuth.netinfo import choose_latency_backend, choose_trace_backend, degradation_note
def caps(**kw) -> Capabilities: def caps(**kw) -> Capabilities:
@ -69,7 +69,7 @@ def test_degradation_note_on_windows_mentions_the_api_rather_than_sysctl():
import pytest import pytest
from netcheck.netinfo import iface_for_ip, is_tunnel_iface, mtu_anomaly from netsleuth.netinfo import iface_for_ip, is_tunnel_iface, mtu_anomaly
def test_iface_for_ip_matches_the_owning_adapter(): def test_iface_for_ip_matches_the_owning_adapter():

View file

@ -5,8 +5,8 @@ import asyncio
import httpx import httpx
import pytest import pytest
from netcheck.models import ModuleResult, ProbeError from netsleuth.models import ModuleResult, ProbeError
from netcheck.orchestration import classify_exception, gather_modules, run_module from netsleuth.orchestration import classify_exception, gather_modules, run_module
def test_classify_timeout_errors(): def test_classify_timeout_errors():

View file

@ -2,7 +2,7 @@ from __future__ import annotations
import pytest import pytest
from netcheck.probes.latency import summarize_ping from netsleuth.probes.latency import summarize_ping
def test_summarize_builds_a_complete_ping_result(): def test_summarize_builds_a_complete_ping_result():

View file

@ -1,6 +1,6 @@
from __future__ import annotations from __future__ import annotations
from netcheck.reputation import ( from netsleuth.reputation import (
NetsetIndex, NetsetIndex,
build_reputation, build_reputation,
captcha_risk, captcha_risk,

View file

@ -2,7 +2,7 @@ from __future__ import annotations
import pytest import pytest
from netcheck.speed import bufferbloat_delta, mbps, parse_server_timing_cfl4, throughput_from_samples from netsleuth.speed import bufferbloat_delta, mbps, parse_server_timing_cfl4, throughput_from_samples
def test_mbps_converts_bytes_and_seconds(): def test_mbps_converts_bytes_and_seconds():
@ -86,8 +86,8 @@ def test_bufferbloat_delta_needs_both_a_baseline_and_samples():
assert bufferbloat_delta(12.0, []) is None assert bufferbloat_delta(12.0, []) is None
from netcheck.models import SpeedResult, TierAttempt from netsleuth.models import SpeedResult, TierAttempt
from netcheck.speed import NDT7_CONSENT_NOTICE, run_speed_cascade from netsleuth.speed import NDT7_CONSENT_NOTICE, run_speed_cascade
async def test_cascade_stops_at_the_first_tier_that_returns_a_download_figure(): async def test_cascade_stops_at_the_first_tier_that_returns_a_download_figure():
@ -171,8 +171,8 @@ def test_the_ndt7_consent_notice_states_what_gets_published():
assert "public" in NDT7_CONSENT_NOTICE.lower() assert "public" in NDT7_CONSENT_NOTICE.lower()
from netcheck.config import BufferbloatBands from netsleuth.config import BufferbloatBands
from netcheck.speed import measure_with_bufferbloat, probe_while from netsleuth.speed import measure_with_bufferbloat, probe_while
async def test_probe_while_collects_samples_for_the_whole_duration_of_the_work(): async def test_probe_while_collects_samples_for_the_whole_duration_of_the_work():

View file

@ -4,7 +4,7 @@ import math
import pytest import pytest
from netcheck.stats import percentile, rtt_stats from netsleuth.stats import percentile, rtt_stats
def test_all_samples_present(): def test_all_samples_present():

View file

@ -2,7 +2,7 @@ from __future__ import annotations
import pytest import pytest
from netcheck.traceparse import parse_linux from netsleuth.traceparse import parse_linux
@pytest.fixture() @pytest.fixture()
@ -73,7 +73,7 @@ def test_linux_parser_tolerates_empty_and_garbage_input():
assert parse_linux("bash: traceroute: command not found\n") == [] assert parse_linux("bash: traceroute: command not found\n") == []
from netcheck.traceparse import WINDOWS_SUB_MS, parse_windows from netsleuth.traceparse import WINDOWS_SUB_MS, parse_windows
@pytest.fixture() @pytest.fixture()
@ -143,7 +143,7 @@ def test_windows_parser_tolerates_empty_input():
assert parse_windows("") == [] assert parse_windows("") == []
from netcheck.traceparse import parse_darwin from netsleuth.traceparse import parse_darwin
def test_darwin_basic_hops_match_the_gnu_shape(trace_fixture): def test_darwin_basic_hops_match_the_gnu_shape(trace_fixture):
@ -185,7 +185,7 @@ def test_darwin_parser_tolerates_empty_input():
assert parse_darwin("") == [] assert parse_darwin("") == []
from netcheck.traceparse import build_trace_result, parse_traceroute from netsleuth.traceparse import build_trace_result, parse_traceroute
def test_dispatcher_routes_by_os_name(trace_fixture): def test_dispatcher_routes_by_os_name(trace_fixture):

View file

@ -5,9 +5,9 @@ import threading
import pytest import pytest
from netcheck.models import Capabilities, TraceHop, TraceResult from netsleuth.models import Capabilities, TraceHop, TraceResult
from netcheck.probes.icmp_win import IP_REQ_TIMED_OUT, IP_SUCCESS, IP_TTL_EXPIRED_TRANSIT, IcmpReply from netsleuth.probes.icmp_win import IP_REQ_TIMED_OUT, IP_SUCCESS, IP_TTL_EXPIRED_TRANSIT, IcmpReply
from netcheck.probes.traceroute import _run_in_daemon_thread, hops_from_win_replies, run_cascade, tier_order from netsleuth.probes.traceroute import _run_in_daemon_thread, hops_from_win_replies, run_cascade, tier_order
def caps(**kw) -> Capabilities: def caps(**kw) -> Capabilities:

View file

@ -5,8 +5,8 @@ from pathlib import Path
import pytest import pytest
from netcheck.models import PingResult, SpeedResult from netsleuth.models import PingResult, SpeedResult
from netcheck.watch import ( from netsleuth.watch import (
WATCH_SCHEMA_VERSION, WATCH_SCHEMA_VERSION,
WatchSession, WatchSession,
is_speedtest_cycle, is_speedtest_cycle,

2
uv.lock generated
View file

@ -208,7 +208,7 @@ wheels = [
] ]
[[package]] [[package]]
name = "netcheck" name = "netsleuth"
version = "0.1.0" version = "0.1.0"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [