fix(client): flush the device code, or nobody ever sees it

Hit for real driving a first sign-in: `granthi-sync link` run through anything
that is not a terminal -- a wrapper, a pipe, `| tee setup.log` -- printed
NOTHING, while the code it was holding silently expired five minutes later.
Python buffers stdout when stdout is not a tty, and the two prints carrying
the verification URL and the user code did not flush.

They flush now. One regression test asserts both are printed with flush=True,
because this fails silently and only under redirection, which is exactly the
condition a test would otherwise never reproduce.

204 tests (was 203).
This commit is contained in:
claude
2026-08-23 15:58:34 -04:00
parent 6d62419fc7
commit e77c0077e3
2 changed files with 33 additions and 2 deletions
+26
View File
@@ -1284,5 +1284,31 @@ class TestResolveGranted(unittest.TestCase):
client.resolve_granted(self.cfg, "Nirlabinc/Ai-Assistant"),
"Nirlabinc/Ai-Assistant")
class TestDeviceFlowOutputIsVisible(unittest.TestCase):
"""The code has a five-minute life. If it is sitting in a buffer, the user
never sees it and it expires -- which is what happened on 2026-08-23 while
driving a first real sign-in through a wrapper."""
def test_the_url_and_code_are_flushed_immediately(self):
seen = []
real_print = print
def spy(*a, **kw):
seen.append(kw.get("flush", False))
return real_print(*a, **{k: v for k, v in kw.items() if k != "flush"})
resp = {"verification_uri_complete": "https://id.example/device?user_code=AB-CD",
"user_code": "AB-CD", "device_code": "dc", "interval": 0,
"expires_in": 0}
with mock.patch.object(client, "http_json", lambda *a, **k: (200, resp)), \
mock.patch("builtins.print", spy), \
mock.patch.object(client.time, "sleep", lambda *_: None):
with self.assertRaises(SystemExit): # expires_in 0 -> times out
client.device_flow()
self.assertTrue(seen, "device_flow printed nothing")
self.assertTrue(all(seen[:2]),
"the URL and code must be printed with flush=True")
if __name__ == "__main__":
unittest.main()