aboutsummaryrefslogtreecommitdiffstats
path: root/test_llamachat.py
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-08-09 14:52:25 +0200
committerDanilo M. <danix@danix.xyz>2026-08-09 14:52:25 +0200
commit22e177e4d2723df8612fbad2851c03d66724b2c4 (patch)
treef5c26c30121d2d5482f7dfaf1355aa132276cfc5 /test_llamachat.py
parent955fca181f4b3eac9732c94835add054499964c8 (diff)
downloadllamachat-22e177e4d2723df8612fbad2851c03d66724b2c4.tar.gz
llamachat-22e177e4d2723df8612fbad2851c03d66724b2c4.zip
fix: sharpen key resolution errors, rename KeyResolutionError
Raise KEY_TIMEOUT to 120s: a graphical pinentry plus a hardware token the user has to find and touch makes 30s a plausible successful unlock, and failing one converts a slow success into an error the user can only retry against the same clock. Point the timeout message at a pinentry that never appeared, which is the silent failure, rather than at one the user is already looking at. Name the binary when pass is missing instead of reporting a bare Errno 2. Raise from None so a partial secret on the failed process stdout stays out of printed tracebacks, and record on the cache that it is unlocked only because every caller resolves on the GUI thread today. Rename KeyError_ to KeyResolutionError before later tasks import it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'test_llamachat.py')
-rwxr-xr-xtest_llamachat.py48
1 files changed, 44 insertions, 4 deletions
diff --git a/test_llamachat.py b/test_llamachat.py
index 08f31c9..5ee2310 100755
--- a/test_llamachat.py
+++ b/test_llamachat.py
@@ -1098,7 +1098,7 @@ def test_key_resolution():
try:
resolver.resolve(missing)
assert False, "a missing env var must raise"
- except providers.KeyError_ as exc:
+ except providers.KeyResolutionError as exc:
assert "gone" in str(exc)
# pass: shells out. Substitute the runner rather than requiring gpg.
@@ -1139,9 +1139,25 @@ def test_key_resolution():
try:
providers.KeyResolver(runner=boom).resolve(passed)
assert False, "a failing pass must raise"
- except providers.KeyError_ as exc:
+ except providers.KeyResolutionError as exc:
assert "together" in str(exc)
+ # A timeout points at the pinentry never appearing, which is the silent
+ # case, rather than at one the user can already see.
+ def slow(cmd, timeout):
+ raise subprocess.TimeoutExpired(cmd, timeout, output="partial-secret")
+
+ try:
+ providers.KeyResolver(runner=slow).resolve(passed)
+ assert False, "a pass timeout must raise"
+ except providers.KeyResolutionError as exc:
+ assert "gpg-agent" in str(exc)
+ # The partial stdout a timeout captures must never reach the message.
+ assert "partial-secret" not in str(exc)
+ # `from None` suppresses the chained traceback. It does not clear
+ # __context__, so this pins the display behavior, not unreachability.
+ assert exc.__suppress_context__ and exc.__cause__ is None
+
# A non-zero exit quotes gpg's stderr, which is the only useful part, and
# never stdout, which is where the secret would be.
def refused(cmd, timeout):
@@ -1153,15 +1169,39 @@ def test_key_resolution():
try:
providers.KeyResolver(runner=refused).resolve(passed)
assert False, "a non-zero pass exit must raise"
- except providers.KeyError_ as exc:
+ except providers.KeyResolutionError as exc:
assert "No secret key" in str(exc)
assert "sk-test-not-a-real-key" not in str(exc)
+ # An empty or absent stderr falls back to the exit status rather than
+ # reporting a blank reason.
+ for blank in ("", None):
+ def quiet(cmd, timeout, _s=blank):
+ raise subprocess.CalledProcessError(3, cmd, output="", stderr=_s)
+
+ try:
+ providers.KeyResolver(runner=quiet).resolve(passed)
+ assert False, "a non-zero pass exit must raise"
+ except providers.KeyResolutionError as exc:
+ assert "exit status 3" in str(exc)
+ assert exc.__suppress_context__ and exc.__cause__ is None
+
+ # A missing `pass` binary names the binary, not just "No such file".
+ def absent(cmd, timeout):
+ raise FileNotFoundError(2, "No such file or directory", "pass")
+
+ try:
+ providers.KeyResolver(runner=absent).resolve(passed)
+ assert False, "a missing pass binary must raise"
+ except providers.KeyResolutionError as exc:
+ assert "not installed" in str(exc)
+ assert "together" in str(exc)
+
# Empty output is a failure too: an empty key would 401 confusingly.
try:
providers.KeyResolver(runner=lambda cmd, timeout: " \n").resolve(passed)
assert False, "empty pass output must raise"
- except providers.KeyError_:
+ except providers.KeyResolutionError:
pass
print("ok api key resolution")