fix(dub): name the source language code that was rejected
Closes #1960. The report was "400 Bad Request: Invalid source language code" and nothing else. That cannot be acted on or triaged: it does not say which of the ninety or so codes was wrong, so neither the user nor a maintainer reading the auto-filed issue can tell whether the picker offered something the backend does not accept, or a stale preference from an older build is still being sent. I could not determine the cause from the report, which is exactly the problem. Naming the code makes the next one answerable instead of guessing at this one. The value is a language code chosen from a menu, not private data, and the engine validator a few lines away already echoes its input the same way. Also adds the check I actually wanted while investigating: a test that reads the picker's own LANG_CODES and asserts the backend accepts every one of them, so a code added to the menu cannot silently become a 400. It passes today — the menu and the allowlist do agree — which is how I ruled that out as the cause rather than assuming it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ypcgSsh5j2PEonSJiAU1S
This commit is contained in:
co-authored by
Claude Opus 5
parent
f905230fd8
commit
d1e4c847cb
@@ -9,6 +9,7 @@ the frozen-backend fallback mirror it for their toolchains.
|
||||
## [Unreleased]
|
||||
|
||||
**Highlights**
|
||||
- A rejected dubbing source language now names the code it rejected (#1960)
|
||||
- `bun run desktop` reclaims port 3900 from a backend the app itself left running, instead of refusing to start (#1974)
|
||||
- A dictation shortcut another app already owns now says so, instead of silently doing nothing (#1858)
|
||||
- Quitting on Windows is no longer reported as a crash on the next launch (#1898)
|
||||
|
||||
@@ -540,12 +540,30 @@ _DUB_SOURCE_LANG_CODES = frozenset({
|
||||
|
||||
|
||||
def _source_lang_override(value: str | None) -> str | None:
|
||||
"""Normalize a user-selected source language; auto/und means detect."""
|
||||
"""Normalize a user-selected source language; auto/und means detect.
|
||||
|
||||
A rejection NAMES the code it rejected. "Invalid source language code" on
|
||||
its own cannot be acted on or reported usefully: it does not say which of
|
||||
the ninety-odd codes was wrong, so neither the user nor a maintainer
|
||||
reading the auto-filed issue can tell whether the picker offered something
|
||||
the backend does not accept, or a stale preference from an older build is
|
||||
still being sent (#1960).
|
||||
|
||||
The value is a language code the user chose from a menu — not private
|
||||
data — and the neighbouring engine validator already echoes its input the
|
||||
same way.
|
||||
"""
|
||||
code = (value or "").strip().lower()
|
||||
if code in {"", "auto", "und"}:
|
||||
return None
|
||||
if code not in _DUB_SOURCE_LANG_CODES:
|
||||
raise HTTPException(status_code=400, detail="Invalid source language code")
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=(
|
||||
f"Invalid source language code: {code!r}. Pick a language from "
|
||||
"the Dubbing source-language menu, or leave it on auto-detect."
|
||||
),
|
||||
)
|
||||
return code
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
"""#1960 — a rejected source language must say WHICH code it rejected.
|
||||
|
||||
The report was "400 Bad Request: Invalid source language code" and nothing
|
||||
else. That cannot be acted on or triaged: it does not say which of the ninety
|
||||
or so codes was wrong, so neither the user nor a maintainer reading the
|
||||
auto-filed issue can tell whether the picker offered something the backend
|
||||
does not accept, or a stale preference from an older build is still being
|
||||
sent. I could not determine the cause from the report — which is the point.
|
||||
|
||||
The value is a language code chosen from a menu, not private data, and the
|
||||
neighbouring engine validator already echoes its input the same way.
|
||||
|
||||
The last test is the durable one: it reads the picker's own list and asserts
|
||||
the backend accepts all of it, so a code added to the menu cannot silently
|
||||
become a 400.
|
||||
"""
|
||||
import pathlib
|
||||
import re
|
||||
|
||||
import pytest
|
||||
from fastapi import HTTPException
|
||||
|
||||
from api.routers.dub_core import _DUB_SOURCE_LANG_CODES, _source_lang_override
|
||||
|
||||
_REPO = pathlib.Path(__file__).resolve().parents[1]
|
||||
_LANGUAGES_JS = _REPO / "frontend" / "src" / "utils" / "languages.js"
|
||||
|
||||
|
||||
def test_the_rejection_names_the_code():
|
||||
with pytest.raises(HTTPException) as caught:
|
||||
_source_lang_override("zz-XX")
|
||||
detail = caught.value.detail
|
||||
assert "zz-xx" in detail
|
||||
assert caught.value.status_code == 400
|
||||
|
||||
|
||||
def test_the_rejection_says_what_to_do():
|
||||
with pytest.raises(HTTPException) as caught:
|
||||
_source_lang_override("nope")
|
||||
assert "auto-detect" in caught.value.detail
|
||||
|
||||
|
||||
@pytest.mark.parametrize("code", ["", " ", "auto", "und", None])
|
||||
def test_detect_is_still_detect(code):
|
||||
assert _source_lang_override(code) is None
|
||||
|
||||
|
||||
def test_accepted_codes_are_unchanged():
|
||||
# This only improved a message; it must not start accepting or rejecting
|
||||
# anything different.
|
||||
for code in sorted(_DUB_SOURCE_LANG_CODES):
|
||||
assert _source_lang_override(code) == code
|
||||
|
||||
|
||||
def test_every_code_the_picker_offers_is_accepted():
|
||||
src = _LANGUAGES_JS.read_text(encoding="utf-8")
|
||||
codes = re.findall(r"code:\s*['\"]([A-Za-z-]+)['\"]", src)
|
||||
assert len(codes) > 50, "LANG_CODES did not parse; update this extraction"
|
||||
rejected = []
|
||||
for code in codes:
|
||||
try:
|
||||
_source_lang_override(code)
|
||||
except HTTPException:
|
||||
rejected.append(code)
|
||||
assert rejected == [], (
|
||||
"the Dubbing source-language menu offers codes the backend rejects, so "
|
||||
f"picking them returns 400: {rejected}"
|
||||
)
|
||||
Reference in New Issue
Block a user