Ctrl K
ComunidadGuías…

tests/agents/test_plugin_secrets.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
"""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")