From fa8928b68595af9a86c823b1db8d2d10dbefce3d Mon Sep 17 00:00:00 2001 From: Paulo Date: Sun, 16 Aug 2026 15:37:53 +0200 Subject: [PATCH] A declared browser session is stored the first time a run reaches for it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Until a workflow borrows the login, the declaration is the only thing that exists — nothing is written at import or install. The first borrow writes the row, so the sessions pane can show the operator which login the extension is waiting on, and the borrow itself says the session is not ready yet. Reporting a bounced login lands on the same row. --- backend/druks/browser/sessions.py | 22 +++++++++++++++------- backend/tests/test_browser_borrow.py | 20 ++++++++++++-------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/backend/druks/browser/sessions.py b/backend/druks/browser/sessions.py index e244b656..a0a08142 100644 --- a/backend/druks/browser/sessions.py +++ b/backend/druks/browser/sessions.py @@ -14,7 +14,6 @@ BrowserExportError, BrowserLaunchError, BrowserSessionNotReadyError, - BrowserSessionUnknownError, ) from druks.browser.locks import acquire_writer_lock, release_writer_lock from druks.browser.models import StoredBrowserSession @@ -100,15 +99,24 @@ async def playwright(self): def mark_stale(self) -> None: """Report the login bounced — the site wants the operator back. The pane shows the session as stale; the workflow decides whether to park.""" + self._get_or_create_row().mark_stale() + + def _get_or_create_row(self) -> StoredBrowserSession: + """The declaration's stored half, written the first time a run reaches + for the login. Until then the declaration is the only thing that exists — + the row appears in the sessions pane wanting a login, which is how the + operator learns the extension needs one.""" row = StoredBrowserSession.get_for_name(self.name) - if not row: - raise BrowserSessionUnknownError(self.name) - row.mark_stale() + if row: + return row + return StoredBrowserSession.create( + name=self.name, + payload_format=BrowserSessionPayloadFormat.PROFILE_DIR, + site=self.site, + ) def _ready_row(self) -> StoredBrowserSession: - row = StoredBrowserSession.get_for_name(self.name) - if not row: - raise BrowserSessionUnknownError(self.name) + row = self._get_or_create_row() if row.status != BrowserSessionStatus.READY.value: raise BrowserSessionNotReadyError(self.name, row.status) return row diff --git a/backend/tests/test_browser_borrow.py b/backend/tests/test_browser_borrow.py index 29246eba..7e312b6a 100644 --- a/backend/tests/test_browser_borrow.py +++ b/backend/tests/test_browser_borrow.py @@ -9,7 +9,6 @@ BrowserClientMissingError, BrowserLaunchError, BrowserSessionNotReadyError, - BrowserSessionUnknownError, BrowserSessionWriterLockedError, ) from druks.browser.models import StoredBrowserSession @@ -178,20 +177,25 @@ async def test_persisting_borrow_refuses_a_second_writer(borrow): assert browser.commands == [] -async def test_borrow_requires_a_ready_session(borrow, druks_db): - with pytest.raises(BrowserSessionUnknownError): +async def test_first_borrow_writes_the_declared_session_and_asks_for_a_login(borrow, druks_db): + """Nothing is stored until a run reaches for the login: the first borrow + writes the row so the pane can ask the operator to sign in, and says so.""" + assert not StoredBrowserSession.get_for_name(XMe.docs.name) + + with pytest.raises(BrowserSessionNotReadyError): async with XMe.docs.cdp(): pass - StoredBrowserSession.create( - name=XMe.docs.name, - payload_format=BrowserSessionPayloadFormat.STORAGE_STATE, - site=XMe.docs.site, - ) + row = StoredBrowserSession.get_for_name(XMe.docs.name) + assert row.status == BrowserSessionStatus.NEEDS_LOGIN.value + assert row.site == XMe.docs.site + with pytest.raises(BrowserSessionNotReadyError): async with XMe.docs.cdp(): pass + assert StoredBrowserSession.list_all() == [row] + async def test_launch_failure_raises_and_releases_the_lock(borrow): browser, redis = borrow