crdt-sync: seed kartka-agent and kartka-source-eval desktop sessions

kartka's headless agent wrapper serves ~/.config/kartka-agent/
firebase_auth.json once per launch as /sync-credentials, and its weekly
source evaluator reads credential_store_for("kartka-source-eval"), which
otherwise falls back to the retired password. One run with
--app kartka-agent --app kartka-source-eval seeds both from one consent
tap. Both also join the default all-app run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S2brk4NNnRyTnt7cD2QF2f
This commit is contained in:
Krzysztof Rudnicki 2026-10-25 08:13:26 +01:00
parent f93a24e283
commit ea5be2e879
2 changed files with 67 additions and 0 deletions

View File

@ -0,0 +1,59 @@
# Copyright (c) 2026 Krzysztof Rudnicki
"""kartka's two desktop sessions are seeded by one ``seed_session`` run.
Split from :mod:`test_tool_seed_session_main` for the 250-line cap.
"""
from __future__ import annotations
from datetime import UTC, datetime
import json
from pathlib import Path
from unittest.mock import patch
import pytest
from crdt_sync._config import credential_store_for
from crdt_sync._credentials import FirebaseCredentials
from crdt_sync.tests.test_tool_seed_session import _CONFIG, _ID_VALUE
from tool import google_id_token as git
from tool import seed_session as ss
_ARGV = ["--client-id", "the-web-client-id", "--client-secret", "the-secret"]
_KARTKA = ("kartka-agent", "kartka-source-eval")
def test_both_kartka_sessions_are_default_apps() -> None:
"""The agent wrapper serves ``~/.config/kartka-agent/`` as
``/sync-credentials``; the source evaluator reads
``credential_store_for("kartka-source-eval")``."""
assert set(_KARTKA) <= set(ss.DEFAULT_APPS)
def test_one_run_with_two_app_flags_seeds_both() -> None:
"""``--app`` is repeatable, so one consent tap covers both."""
with (
patch.object(ss.FirebaseConfig, "load", return_value=_CONFIG),
patch.object(git, "fetch_id_token", return_value=_ID_VALUE),
patch.object(ss, "seed_apps", return_value=list(_KARTKA)) as seeded,
):
code = ss.main([*_ARGV, "--app", _KARTKA[0], "--app", _KARTKA[1]])
assert code == 0
assert seeded.call_args.args[2] == _KARTKA
@pytest.mark.parametrize("app", _KARTKA)
def test_the_seed_lands_where_kartka_reads_it(app: str, tmp_path: Path) -> None:
"""``~/.config/<app>/firebase_auth.json``, the path both readers use, in
the ``{id_token, refresh_token, expires_at}`` shape the wrapper serves."""
with patch.object(Path, "home", return_value=tmp_path):
credential_store_for(app).save(
FirebaseCredentials("fake-id", "fake-refresh", datetime.now(UTC))
)
written = tmp_path / ".config" / app / "firebase_auth.json"
assert set(json.loads(written.read_text())) == {
"id_token",
"refresh_token",
"expires_at",
}

View File

@ -59,4 +59,12 @@ DEFAULT_APPS: Final = (
# the Flutter client over RTDB (/sufler), headless, reading
# ~/.config/sufler/firebase_auth.json via credential_store_for("sufler").
"sufler",
# ~/src/kartka's headless agent instance (tools/agent): its wrapper serves
# this file once per launch as /sync-credentials, so the web build signs
# in with no password and no browser consent of its own.
"kartka-agent",
# ~/src/kartka/tools/source_eval: the weekly source evaluator publishes
# /kartka/source_health via credential_store_for("kartka-source-eval")
# and falls back to the retired password only with no session here.
"kartka-source-eval",
)