Files
hermes-agent/tests/hermes_cli/test_imagegen_managed_gateway.py
T
Teknium ba030bc0db fix(test-seams): monkeypatch.setattr facade aliases — also patch the defining module (57 files)
Tests did monkeypatch.setattr(<facade module>, name) where name is now defined in a
sibling module and the production path reads the sibling's binding. Where production
reads through BOTH bindings the setattr is duplicated onto the defining module (import
added next to the existing alias import); where only the sibling reads it the target is
repointed. Seams whose production readers go through the facade are left alone.
2026-09-03 19:33:28 -07:00

141 lines
6.0 KiB
Python

"""Regression tests for image_gen provider persistence (managed FAL clobber).
Historical bug: ``_select_plugin_image_gen_provider`` hardcoded the direct
(non-managed) routing. When a user picked FAL through the Nous-subscription
managed flow, the managed write landed first — then the image selector ran
and clobbered it back to direct, silently routing every generation through
the user's personal FAL_KEY instead of the Nous Tool Gateway (real incident:
personal key drained to zero while the subscription sat unused).
Current contract (strict provider-string selection): each picker row writes
exactly ONE provider string per category — ``image_gen.provider: nous`` for
the managed "Nous Subscription" row, ``image_gen.provider: fal`` for the
BYOK FAL row — and any legacy ``use_gateway`` key is popped so the
read-time shim (use_gateway: true ⇒ nous) cannot override the fresh pick.
The video twin (``_select_plugin_video_gen_provider``) shares the contract.
"""
from hermes_cli.tools_config import (
_select_plugin_image_gen_provider,
_select_plugin_video_gen_provider,
_write_provider_config,
)
def _quiet(monkeypatch):
import hermes_cli.tools_config_providers as tc
monkeypatch.setattr(tc, "_print_success", lambda *a, **k: None)
monkeypatch.setattr(tc, "_print_info", lambda *a, **k: None, raising=False)
monkeypatch.setattr(tc, "_configure_imagegen_model_for_plugin", lambda *a, **k: None)
monkeypatch.setattr(tc, "_configure_videogen_model_for_plugin", lambda *a, **k: None)
def test_image_gen_selector_preserves_managed_selection(monkeypatch):
"""Managed pick: the 'nous' provider string must survive the selector."""
_quiet(monkeypatch)
config = {}
# The managed flow first persists the managed selection...
_write_provider_config(
{"image_gen_plugin_name": "fal"}, config, managed_feature="image_gen"
)
assert config["image_gen"]["provider"] == "nous"
assert "use_gateway" not in config["image_gen"]
# ...then the selector runs; the managed kwarg must NOT clobber it
# back onto the vendor name (direct-key routing).
_select_plugin_image_gen_provider("fal", config, use_gateway=True)
assert config["image_gen"]["provider"] == "nous"
assert "use_gateway" not in config["image_gen"]
def test_image_gen_selector_direct_key_pick_writes_vendor(monkeypatch):
"""Non-managed pick writes the vendor name and pops the legacy flag."""
_quiet(monkeypatch)
config = {"image_gen": {"use_gateway": True}}
_select_plugin_image_gen_provider("fal", config)
assert config["image_gen"]["provider"] == "fal"
assert "use_gateway" not in config["image_gen"]
def test_image_and_video_selectors_share_the_selection_contract(monkeypatch):
"""The two selectors are twins: same kwarg, same persistence behavior."""
_quiet(monkeypatch)
for use_gateway, expected in ((True, "nous"), (False, "fal")):
config = {
"image_gen": {"use_gateway": not use_gateway},
"video_gen": {"use_gateway": not use_gateway},
}
_select_plugin_image_gen_provider("fal", config, use_gateway=use_gateway)
_select_plugin_video_gen_provider("fal", config, use_gateway=use_gateway)
assert config["image_gen"]["provider"] == expected
assert config["video_gen"]["provider"] == expected
assert "use_gateway" not in config["image_gen"]
assert "use_gateway" not in config["video_gen"]
def _quiet_reconfigure(monkeypatch):
"""Silence prints + model pickers for _reconfigure_provider paths."""
import hermes_cli.tools_config as tc
import hermes_cli.tools_config_post_setup as tools_config_post_setup
import hermes_cli.tools_config_providers as tcp
monkeypatch.setattr(tcp, "_print_success", lambda *a, **k: None)
monkeypatch.setattr(tcp, "_print_info", lambda *a, **k: None, raising=False)
monkeypatch.setattr(tcp, "_print_warning", lambda *a, **k: None, raising=False)
monkeypatch.setattr(tcp, "_configure_imagegen_model", lambda *a, **k: None)
# _configure_provider resolves the post-setup hook lazily from tools_config.
monkeypatch.setattr(tc, "_run_post_setup", lambda *a, **k: None, raising=False)
monkeypatch.setattr(tools_config_post_setup, "_run_post_setup", lambda *a, **k: None, raising=False)
# Managed rows gate on live Portal auth — stub it green.
import hermes_cli.nous_subscription as ns
monkeypatch.setattr(ns, "ensure_nous_portal_access", lambda **k: True)
def test_reconfigure_managed_fal_row_keeps_managed_selection(monkeypatch):
"""The sibling bug of fe63353cb: the legacy-backend model-pick step in
_reconfigure_provider hardcoded the direct selection AFTER the managed
branch wrote the managed one — a Nous Subscription user re-entering the
picker to change models was silently flipped onto their personal
FAL_KEY."""
_quiet_reconfigure(monkeypatch)
import hermes_cli.tools_config as tc
managed_row = {
"name": "Nous Subscription",
"env_vars": [],
"requires_nous_auth": True,
"managed_nous_feature": "image_gen",
"override_env_vars": ["FAL_KEY"],
"imagegen_backend": "fal",
}
config = {"image_gen": {"model": "fal-ai/gpt-image-2", "use_gateway": True}}
tc._reconfigure_provider(managed_row, config)
assert config["image_gen"]["provider"] == "nous"
assert "use_gateway" not in config["image_gen"]
def test_reconfigure_direct_fal_row_writes_vendor_selection(monkeypatch):
"""Direct-key FAL reconfig writes the vendor name and pops any stale
legacy use_gateway key so the read-time shim can't resurrect 'nous'."""
_quiet_reconfigure(monkeypatch)
import hermes_cli.tools_config as tc
direct_row = {
"name": "FAL.ai",
"env_vars": [],
"imagegen_backend": "fal",
}
config = {"image_gen": {"use_gateway": True}}
tc._reconfigure_provider(direct_row, config)
assert config["image_gen"]["provider"] == "fal"
assert "use_gateway" not in config["image_gen"]