Files
clawdmeter/daemon/tests/test_provider_switch.py
T
wenilandClaude Opus 4.8 1b14c363c2 v3 M1c (host): provider selector — control panel + live-switch plumbing
Panel: new Providers tab (data-driven from GET /api/providers — labels/accents
from the daemon registry, enable/active/creds from config). z.ai base_url + key,
api_key masked and preserved on save like the HA token.

Daemon: request_provider_switch() lets the panel push an active-provider change
into the live BLE session (same _set_active_provider path as the watch) so it
takes effect now, not on the next 60s poll; _active_session exposes the session
to the panel thread. On-watch cycle groundwork: provnext -> _cycle_provider
(daemon owns the enabled set/order), and pnm/pi/pc pushed in the payload so the
watch's Provider screen can show the name + "1/3".

server.py: ConfigIn gains providers/active_provider/display_order; secrets masked
in _masked; active_provider change notifies the injected _command_sink.
tray wires set_command_sink(request_provider_switch).

Tests: test_server_providers (masking + sink), test_provider_switch (cycle +
offline persist). 106 passed, 2 Linux-only failures (baseline).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 06:41:19 +03:00

83 lines
3.1 KiB
Python

"""v3 M1c — provider switching (on-watch cycle + control-panel offline hook).
The daemon owns the enabled set/order, so the watch's "switch" tap just asks it
to advance (provnext -> _cycle_provider) and the panel's active_provider edit is
mirrored into the live loop (request_provider_switch). Both funnel through the
same _set_active_provider path exercised here without a real BLE loop.
"""
import json
from unittest.mock import MagicMock
from daemon import config
def _cfg(tmp_path, monkeypatch, data: dict):
p = tmp_path / "config.json"
p.write_text(json.dumps(data), encoding="utf-8")
monkeypatch.setenv("CLAWDMETER_CONFIG", str(p))
monkeypatch.setenv("LOCALAPPDATA", str(tmp_path))
return p
def test_enabled_ids_defaults_to_anthropic(tmp_path, monkeypatch):
from daemon import claude_usage_daemon_windows as d
_cfg(tmp_path, monkeypatch, {"version": 2})
assert d._enabled_ids() == ["anthropic"]
def test_cycle_advances_and_wraps(tmp_path, monkeypatch):
from daemon import claude_usage_daemon_windows as d
_cfg(tmp_path, monkeypatch, {
"version": 2,
"display_order": ["anthropic", "openai", "zai"],
"providers": {"anthropic": {"enabled": True},
"openai": {"enabled": True},
"zai": {"enabled": True}},
"active_provider": "anthropic"})
sess = d.Session(MagicMock())
sess._cycle_provider()
assert config.active_provider_id() == "openai"
sess._cycle_provider()
assert config.active_provider_id() == "zai"
sess._cycle_provider() # wraps back to the start
assert config.active_provider_id() == "anthropic"
def test_cycle_skips_disabled(tmp_path, monkeypatch):
from daemon import claude_usage_daemon_windows as d
_cfg(tmp_path, monkeypatch, {
"version": 2,
"display_order": ["anthropic", "openai", "zai"],
"providers": {"anthropic": {"enabled": True},
"openai": {"enabled": False},
"zai": {"enabled": True}},
"active_provider": "anthropic"})
sess = d.Session(MagicMock())
sess._cycle_provider()
assert config.active_provider_id() == "zai" # openai skipped (disabled)
def test_cycle_single_enabled_is_noop(tmp_path, monkeypatch):
from daemon import claude_usage_daemon_windows as d
_cfg(tmp_path, monkeypatch, {"version": 2, "active_provider": "anthropic"})
sess = d.Session(MagicMock())
sess._cycle_provider()
assert config.active_provider_id() == "anthropic"
def test_request_switch_offline_persists(tmp_path, monkeypatch):
from daemon import claude_usage_daemon_windows as d
_cfg(tmp_path, monkeypatch, {"version": 2, "active_provider": "anthropic"})
monkeypatch.setattr(d, "_active_session", None)
d.request_provider_switch("zai")
assert config.active_provider_id() == "zai"
def test_request_switch_ignores_bad_id(tmp_path, monkeypatch):
from daemon import claude_usage_daemon_windows as d
_cfg(tmp_path, monkeypatch, {"version": 2, "active_provider": "anthropic"})
monkeypatch.setattr(d, "_active_session", None)
d.request_provider_switch("bogus")
assert config.active_provider_id() == "anthropic"