tests/agents/test_plugin_secrets.py
Rama main · réplica de lectura del repositorio original.
"""Secrets in plugin arguments are references (``{env:NAME}``), resolved when
the plugin is made; the reference, not the secret, is what is hashed."""
from __future__ import annotations
import sys
import types
from typing import Any
import pytest
from noeta.agents.agent_files import build_agents, parse_agent_file, resolve_secrets
from noeta.core import registry
from noeta.core.extension import Extension
class Holder(Extension):
name = "holder"
def __init__(self, **kw: Any) -> None:
self.kw = kw
@pytest.fixture(autouse=True)
def holder(monkeypatch: pytest.MonkeyPatch) -> None:
real = registry.load
monkeypatch.setattr(registry, "load",
lambda group, name: Holder if name == "holder" else real(group, name))
AGENT = parse_agent_file("---\nname: a\nplugins: [holder]\n---\n")
ARGS = {"token": "{env:CHAT_TOKEN}", "header": "Bearer {env:CHAT_TOKEN}",
"nested": {"keys": ["{env:CHAT_TOKEN}", 3]}}
def test_env_references_resolve_when_the_plugin_is_made(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("CHAT_TOKEN", "s3cret")
[ext] = build_agents([AGENT], model="m", plugins={"holder": ARGS,
"tool_search": False, "structured_output": False})[0].extensions
assert ext.kw == {"token": "s3cret", "header": "Bearer s3cret", # type: ignore[attr-defined]
"nested": {"keys": ["s3cret", 3]}}
pinned = registry.pin(ext)["arguments"]
monkeypatch.setenv("CHAT_TOKEN", "rotated")
[again] = build_agents([AGENT], model="m", plugins={"holder": ARGS,
"tool_search": False, "structured_output": False})[0].extensions
assert again.kw["token"] == "rotated" # type: ignore[attr-defined]
assert registry.pin(again)["arguments"] == pinned # a rotated secret is the same plugin
def test_an_unset_variable_is_an_error_naming_it(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.delenv("NOPE_TOKEN", raising=False)
with pytest.raises(ValueError, match=r"plugin 'holder': \{env:NOPE_TOKEN\}"):
build_agents([AGENT], model="m", plugins={"holder": {"token": "{env:NOPE_TOKEN}"}})
def test_keyring_references(monkeypatch: pytest.MonkeyPatch) -> None:
fake = types.ModuleType("keyring")
fake.get_password = ( # type: ignore[attr-defined]
lambda service, user: f"{service}:{user}" if user else None)
monkeypatch.setitem(sys.modules, "keyring", fake)
assert resolve_secrets({"t": "{keyring:noeta/chat}"}) == {"t": "noeta:chat"}
with pytest.raises(ValueError, match="no secret"):
resolve_secrets("{keyring:noeta}", "x")
monkeypatch.setitem(sys.modules, "keyring", None)
with pytest.raises(ValueError, match="needs the keyring package"):
resolve_secrets("{keyring:noeta/chat}", "x")