fix(actions): scope scheduled model resolution to owner (#2773)

This commit is contained in:
Ocean Bennett
2026-06-05 07:13:13 -04:00
committed by GitHub
parent 0f8d12363a
commit 2a1febdeef
2 changed files with 161 additions and 7 deletions
+154
View File
@@ -0,0 +1,154 @@
"""Regression tests for owner-scoped model resolution in scheduled actions."""
from datetime import datetime
from types import SimpleNamespace
import pytest
class _Column:
def __eq__(self, _other):
return True
def __ne__(self, _other):
return True
def __ge__(self, _other):
return True
def __le__(self, _other):
return True
class _Query:
def __init__(self, rows):
self._rows = rows
def filter(self, *_args, **_kwargs):
return self
def limit(self, _limit):
return self
def all(self):
return list(self._rows)
class _Db:
def __init__(self, rows_by_model):
self._rows_by_model = rows_by_model
self.commits = 0
self.closed = False
def query(self, model):
return _Query(self._rows_by_model.get(model, []))
def commit(self):
self.commits += 1
def close(self):
self.closed = True
def _resolver_spy(monkeypatch, utility_result=("", "", {}), default_result=("http://llm", "model", {})):
from src import endpoint_resolver
calls = []
fallback_calls = []
def fake_resolve(kind, *args, **kwargs):
calls.append((kind, kwargs.get("owner")))
return utility_result if kind == "utility" else default_result
def fake_fallbacks(*args, **kwargs):
fallback_calls.append(kwargs.get("owner"))
return []
monkeypatch.setattr(endpoint_resolver, "resolve_endpoint", fake_resolve)
monkeypatch.setattr(endpoint_resolver, "resolve_utility_fallback_candidates", fake_fallbacks)
return calls, fallback_calls
@pytest.mark.asyncio
async def test_classify_events_resolves_llm_for_task_owner(monkeypatch):
from core import database
from src.builtin_actions import action_classify_events
class FakeCalendarEvent:
dtstart = _Column()
status = _Column()
event = SimpleNamespace(
summary="Demo presentation",
event_type="work",
importance="high",
color=None,
dtstart=datetime(2026, 1, 1, 9, 0, 0),
location="",
)
db = _Db({FakeCalendarEvent: [event]})
calls, _fallback_calls = _resolver_spy(monkeypatch, utility_result=("http://llm", "model", {}))
monkeypatch.setattr(database, "CalendarEvent", FakeCalendarEvent)
monkeypatch.setattr(database, "SessionLocal", lambda: db)
message, ok = await action_classify_events("alice")
assert ok is True
assert "Scanned 1 upcoming event" in message
assert calls == [("utility", "alice")]
assert db.closed is True
@pytest.mark.asyncio
async def test_learn_sender_signatures_resolves_llm_for_task_owner(monkeypatch):
from routes import email_helpers
from src.builtin_actions import action_learn_sender_signatures
class FakeImap:
def select(self, *_args, **_kwargs):
return "OK", []
def search(self, *_args, **_kwargs):
return "OK", [b"1 2 3"]
def fetch(self, _uid, _query):
return "OK", [(None, b"From: Writer <writer@example.com>\r\n\r\n")]
def logout(self):
return None
calls, _fallback_calls = _resolver_spy(monkeypatch, utility_result=("", "", {}), default_result=("", "", {}))
monkeypatch.setattr(email_helpers, "_imap_connect", lambda _account_id=None: FakeImap())
message, ok = await action_learn_sender_signatures("alice")
assert ok is False
assert message == "No LLM endpoint available"
assert calls == [("utility", "alice"), ("default", "alice")]
@pytest.mark.asyncio
async def test_check_email_urgency_resolves_llm_candidates_for_task_owner(monkeypatch, tmp_path):
from core import database
from src.builtin_actions import TaskNoop, action_check_email_urgency
class FakeEmailAccount:
enabled = _Column()
owner = _Column()
imap_user = _Column()
from_address = _Column()
db = _Db({FakeEmailAccount: []})
calls, fallback_calls = _resolver_spy(monkeypatch, utility_result=("http://llm", "model", {}))
monkeypatch.chdir(tmp_path)
monkeypatch.setattr(database, "EmailAccount", FakeEmailAccount)
monkeypatch.setattr(database, "SessionLocal", lambda: db)
with pytest.raises(TaskNoop, match="no email accounts configured"):
await action_check_email_urgency("alice")
assert calls == [("utility", "alice")]
assert fallback_calls == ["alice"]
assert db.closed is True