soulsync/tests/test_profile_context.py
BoulderBadgeDad 6980253a96 Automations: run each as its OWNER profile in the background (part 1 of per-profile sync)
Background automations had no session, so get_current_profile_id() fell back to
admin (1) — wrong for a non-admin's scheduled job. Now the engine declares the
automation's owner around handler execution via a contextvar
(core/profile_context.py), and get_current_profile_id() consults it only when
there's NO web request. So:
- a real logged-in request always wins (foreground unchanged),
- admin + system automations are profile 1 → resolve to admin exactly as before
  (the 8 admin-owned auto-sync pipelines behave identically),
- only non-admin-owned automations gain their correct identity, deep through the
  whole call chain (incl. the per-profile client resolvers) — no threading
  profile_id through dozens of signatures.

Reset in a finally so a pooled thread can't leak the override to the next job.

Tests: contextvar set/reset/nested; get_current_profile_id honours the override
only outside a request (a real session still wins); and end-to-end — the engine
runs a non-admin automation as profile 4, an admin one as 1, an explicit trigger
profile overrides the owner, and the context resets even when the handler raises.
27 + 4 tests pass.

Part 2 (next): point the sync handlers' source-playlist READ at
get_spotify_client_for_profile so a non-admin's auto-sync pulls THEIR playlist.
2026-06-10 15:37:56 -07:00

31 lines
1.1 KiB
Python

"""Background profile context (per-profile automations).
Lets background work (the automation engine) declare which profile it's acting
for, so get_current_profile_id() resolves to the automation's OWNER instead of
admin when there's no web request. A real request must still win; admin/system
(profile 1) and no-override must stay admin.
"""
from __future__ import annotations
from core.profile_context import (
set_background_profile, reset_background_profile, get_background_profile,
)
def test_set_reset_roundtrip():
assert get_background_profile() is None
tok = set_background_profile(5)
assert get_background_profile() == 5
reset_background_profile(tok)
assert get_background_profile() is None
def test_nested_set_reset():
t1 = set_background_profile(3)
t2 = set_background_profile(1) # e.g. an admin/system sub-run
assert get_background_profile() == 1
reset_background_profile(t2)
assert get_background_profile() == 3 # back to the outer profile
reset_background_profile(t1)
assert get_background_profile() is None # fully cleared (no leak)