test: update dirsize/md5 probe with path-variant comparison results
Three path formats tested against NAS: a) plain string -> 599 (wrong, task fails silently) b) json.dumps([path]) -> works (JSON array is the correct format) c) json.dumps(path) -> 599 (wrong, double-encoded string) Confirms current implementation (json.dumps(paths)) is correct. MD5 probe simplified to final confirmed settings (status v1, 0ms). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+45
-115
@@ -1,11 +1,8 @@
|
||||
"""Wegwerfskript: DirSize + MD5 direkt gegen die NAS testen.
|
||||
|
||||
Ergebnisse der Versionsanalyse (vollständig):
|
||||
DirSize: start v2, status v1
|
||||
MD5: start v2, status v2
|
||||
- Wichtig: MD5-Status ist ONE-SHOT -- nach dem ersten erfolgreichen
|
||||
Abruf verfaellt die Task-ID sofort (code 599 bei allen Folgeabfragen).
|
||||
- Erster status-Aufruf muss direkt nach start kommen (delay~0).
|
||||
Finale Befunde:
|
||||
DirSize: start v2 (path als plain string ODER JSON-Array), status v1 (0ms delay)
|
||||
MD5: start v2, status v1 (0ms delay, ONE-SHOT)
|
||||
|
||||
DirSize status-Response Felder:
|
||||
finished: bool
|
||||
@@ -17,14 +14,6 @@ MD5 status-Response Felder:
|
||||
finished: bool
|
||||
md5: str (Hex-String, 32 Zeichen)
|
||||
|
||||
Hypothese: _poll_task() schlaeft 200ms vor dem ersten status-Call.
|
||||
Winzige Daten -> Task abgeschlossen bevor erster Poll -> 599.
|
||||
Fix: initial_delay=0 fuer DirSize und MD5.
|
||||
|
||||
WICHTIG: Rohe HTTP-Tests treffen evt. falschen API-Pfad (entry.cgi),
|
||||
waehrend die API wirklich auf einem anderen Pfad (z.B. FileStation.cgi)
|
||||
laeuft. Test unten zeigt API-Pfad aus dem Cache.
|
||||
|
||||
Ausfuehren: uv run python test_dirsize_md5.py
|
||||
"""
|
||||
|
||||
@@ -51,7 +40,6 @@ def pp(label: str, data: object, elapsed_ms: float | None = None) -> None:
|
||||
|
||||
|
||||
async def raw(http: httpx.AsyncClient, url: str, sid: str, **params) -> dict:
|
||||
"""Hit a specific URL (not hardcoded to entry.cgi)."""
|
||||
r = await http.get(url, params={"_sid": sid, **params})
|
||||
r.raise_for_status()
|
||||
try:
|
||||
@@ -60,66 +48,60 @@ async def raw(http: httpx.AsyncClient, url: str, sid: str, **params) -> dict:
|
||||
return {"_raw": r.text[:300], "_http_status": r.status_code}
|
||||
|
||||
|
||||
async def probe_dirsize(
|
||||
http: httpx.AsyncClient, base: str, sid: str, api_url: str
|
||||
async def probe_dirsize_path_variants(
|
||||
http: httpx.AsyncClient, sid: str, api_url: str
|
||||
) -> None:
|
||||
"""Test all three path encoding variants for DirSize start."""
|
||||
print(f"\n{'#'*60}")
|
||||
print(f" DIRSIZE — Timing-Probe (URL: {api_url})")
|
||||
print(" Delays: 0ms, 50ms, 200ms, 500ms, stale, fake")
|
||||
print(" DIRSIZE path-Varianten (a=plain, b=json.dumps, c=manuell)")
|
||||
print(f"{'#'*60}")
|
||||
|
||||
variants = [
|
||||
("a) plain string", DIRSIZE_PATH),
|
||||
("b) json.dumps([path])", json.dumps([DIRSIZE_PATH])),
|
||||
("c) json.dumps(path)", json.dumps(DIRSIZE_PATH)),
|
||||
]
|
||||
|
||||
for label, path_val in variants:
|
||||
print(f"\n--- Variante {label} ---")
|
||||
print(f" path={path_val!r}")
|
||||
|
||||
t0 = time.perf_counter()
|
||||
start_body = await raw(
|
||||
http, api_url, sid,
|
||||
api="SYNO.FileStation.DirSize", version="2", method="start",
|
||||
path=json.dumps([DIRSIZE_PATH]),
|
||||
path=path_val,
|
||||
)
|
||||
pp("DirSize::start", start_body, (time.perf_counter() - t0) * 1000)
|
||||
pp(f"DirSize::start [{label}]", start_body, (time.perf_counter() - t0) * 1000)
|
||||
|
||||
taskid = (start_body.get("data") or {}).get("taskid")
|
||||
if not taskid:
|
||||
print("[!] No taskid -- aborting probe.")
|
||||
return
|
||||
print(" => Kein taskid (start fehlgeschlagen)")
|
||||
continue
|
||||
|
||||
for label, pre_sleep in [
|
||||
("0ms delay", 0),
|
||||
("~50ms delay", 0.05),
|
||||
("~200ms delay", 0.15),
|
||||
("~500ms delay", 0.3),
|
||||
("stale ~2.5s", 2.0),
|
||||
]:
|
||||
if pre_sleep:
|
||||
await asyncio.sleep(pre_sleep)
|
||||
# Poll immediately
|
||||
t1 = time.perf_counter()
|
||||
r = await raw(
|
||||
status_body = await raw(
|
||||
http, api_url, sid,
|
||||
api="SYNO.FileStation.DirSize", version="1", method="status",
|
||||
taskid=taskid,
|
||||
)
|
||||
pp(f"DirSize::status [{label}]", r, (t1 - t0) * 1000)
|
||||
data = (r.get("data") or {})
|
||||
pp(f"DirSize::status [{label}]", status_body, (t1 - t0) * 1000)
|
||||
|
||||
data = (status_body.get("data") or {})
|
||||
if data.get("finished"):
|
||||
print("[*] finished=True. Stopping here.")
|
||||
break
|
||||
if not r.get("success"):
|
||||
error_code = (r.get("error") or {}).get("code")
|
||||
print(f"[!] Error {error_code}")
|
||||
|
||||
# Fake taskid
|
||||
rf = await raw(
|
||||
http, api_url, sid,
|
||||
api="SYNO.FileStation.DirSize", version="1", method="status",
|
||||
taskid="fake-task-id-does-not-exist",
|
||||
)
|
||||
pp("DirSize::status [fake taskid]", rf)
|
||||
print(f" => OK: num_dir={data.get('num_dir')} "
|
||||
f"num_file={data.get('num_file')} "
|
||||
f"total_size={data.get('total_size')}")
|
||||
else:
|
||||
code = (status_body.get("error") or {}).get("code", "?")
|
||||
print(f" => FEHLER code={code}")
|
||||
|
||||
|
||||
async def probe_md5(
|
||||
http: httpx.AsyncClient, base: str, sid: str, api_url: str
|
||||
) -> None:
|
||||
async def probe_md5(http: httpx.AsyncClient, sid: str, api_url: str) -> None:
|
||||
"""Test MD5 with status v1 at 0ms (correct settings)."""
|
||||
print(f"\n{'#'*60}")
|
||||
print(f" MD5 -- Timing-Probe (URL: {api_url})")
|
||||
print(" Delays: 0ms, 50ms, 200ms, stale, fake")
|
||||
print(" MD5 -- start v2, status v1, 0ms delay")
|
||||
print(f"{'#'*60}")
|
||||
|
||||
t0 = time.perf_counter()
|
||||
@@ -132,71 +114,21 @@ async def probe_md5(
|
||||
|
||||
taskid = (start_body.get("data") or {}).get("taskid")
|
||||
if not taskid:
|
||||
print("[!] No taskid -- aborting probe.")
|
||||
print("[!] No taskid")
|
||||
return
|
||||
|
||||
# Try both versions at 0ms for the same taskid
|
||||
for ver in [1, 2]:
|
||||
t1 = time.perf_counter()
|
||||
r = await raw(
|
||||
http, api_url, sid,
|
||||
api="SYNO.FileStation.MD5", version=str(ver), method="status",
|
||||
api="SYNO.FileStation.MD5", version="1", method="status",
|
||||
taskid=taskid,
|
||||
)
|
||||
pp(f"MD5::status v{ver} [0ms delay]", r, (t1 - t0) * 1000)
|
||||
pp("MD5::status v1 [0ms]", r, (t1 - t0) * 1000)
|
||||
data = (r.get("data") or {})
|
||||
if data.get("finished"):
|
||||
print(f"[*] v{ver} finished=True!")
|
||||
|
||||
for label, pre_sleep in [
|
||||
("~50ms delay", 0.05),
|
||||
("~200ms delay", 0.15),
|
||||
("stale ~2.5s", 2.0),
|
||||
]:
|
||||
await asyncio.sleep(pre_sleep)
|
||||
for ver in [1, 2]:
|
||||
t1 = time.perf_counter()
|
||||
r = await raw(
|
||||
http, api_url, sid,
|
||||
api="SYNO.FileStation.MD5", version=str(ver), method="status",
|
||||
taskid=taskid,
|
||||
)
|
||||
pp(f"MD5::status v{ver} [{label}]", r, (t1 - t0) * 1000)
|
||||
data = (r.get("data") or {})
|
||||
if data.get("finished"):
|
||||
print(f"[*] v{ver} finished=True! md5={data.get('md5')}")
|
||||
|
||||
# What does start response look like for a new task?
|
||||
print("\n[*] Starting a second MD5 task to compare...")
|
||||
t2 = time.perf_counter()
|
||||
start2 = await raw(
|
||||
http, api_url, sid,
|
||||
api="SYNO.FileStation.MD5", version="2", method="start",
|
||||
file_path=json.dumps(MD5_PATH),
|
||||
)
|
||||
pp("MD5::start (2nd)", start2, (time.perf_counter() - t2) * 1000)
|
||||
taskid2 = (start2.get("data") or {}).get("taskid")
|
||||
if taskid2:
|
||||
# Poll immediately with both versions
|
||||
for ver in [1, 2]:
|
||||
t3 = time.perf_counter()
|
||||
r = await raw(
|
||||
http, api_url, sid,
|
||||
api="SYNO.FileStation.MD5", version=str(ver), method="status",
|
||||
taskid=taskid2,
|
||||
)
|
||||
pp(f"MD5::status v{ver} [0ms, task2]", r, (t3 - t2) * 1000)
|
||||
data = (r.get("data") or {})
|
||||
if data.get("finished"):
|
||||
print(f"[*] v{ver} task2 finished=True! md5={data.get('md5')}")
|
||||
|
||||
# Fake taskid
|
||||
rf = await raw(
|
||||
http, api_url, sid,
|
||||
api="SYNO.FileStation.MD5", version="2", method="status",
|
||||
taskid="fake-task-id-does-not-exist",
|
||||
)
|
||||
pp("MD5::status [fake taskid]", rf)
|
||||
print(f" => OK: md5={data.get('md5')}")
|
||||
else:
|
||||
print(f" => FEHLER: {r}")
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
@@ -209,24 +141,22 @@ async def main() -> None:
|
||||
sid = client.sid
|
||||
base = config.base_url
|
||||
|
||||
# --- Show API paths from cache ---
|
||||
print(f"\n[*] SID: {'OK' if sid else 'MISSING'}")
|
||||
for api_name in ["SYNO.FileStation.DirSize", "SYNO.FileStation.MD5"]:
|
||||
info = client._api_cache.get(api_name) # noqa: SLF001
|
||||
if info:
|
||||
print(f"[*] {api_name}: path={info['path']} v{info['minVersion']}-v{info['maxVersion']}")
|
||||
print(f"[*] {api_name}: path={info['path']} "
|
||||
f"v{info['minVersion']}-v{info['maxVersion']}")
|
||||
else:
|
||||
print(f"[!] {api_name}: NOT in API cache!")
|
||||
|
||||
dirsize_info = client._api_cache.get("SYNO.FileStation.DirSize", {}) # noqa: SLF001
|
||||
md5_info = client._api_cache.get("SYNO.FileStation.MD5", {}) # noqa: SLF001
|
||||
|
||||
dirsize_url = f"{base}/webapi/{dirsize_info.get('path', 'entry.cgi')}"
|
||||
md5_url = f"{base}/webapi/{md5_info.get('path', 'entry.cgi')}"
|
||||
|
||||
async with httpx.AsyncClient(verify=config.connection.verify_ssl, timeout=30.0) as http:
|
||||
await probe_dirsize(http, base, sid, dirsize_url)
|
||||
await probe_md5(http, base, sid, md5_url)
|
||||
await probe_dirsize_path_variants(http, sid, dirsize_url)
|
||||
await probe_md5(http, sid, md5_url)
|
||||
|
||||
await auth.logout(client)
|
||||
print("\n[*] Logout OK.")
|
||||
|
||||
Reference in New Issue
Block a user