Files
Octop/tests/unit/test_langfuse_settings.py
347dee56f4 refactor(infra): group agents modules and clarify package ownership (#1164)
* refactor(agents): group thin modules into domain subpackages

Move plugin helpers, memory, settings stores, persona/MBTI, and avatar
bootstrap code under plugins/, memory/, settings/, persona/, and experts/
while keeping top-level import shims so existing call sites stay stable.

Co-authored-by: Cursor <cursoragent@cursor.com>

* refactor(agents): drop compatibility shims after subpackage move

Call sites already import the new package paths; remove the top-level
re-export stubs so the agents tree only shows real modules.

Co-authored-by: Cursor <cursoragent@cursor.com>

* refactor(agents): move thread helpers into threads/

Group artifact shaping, fork, and context breakdown under
agents/threads so top-level only keeps lifecycle/config adapters.

Co-authored-by: Cursor <cursoragent@cursor.com>

* refactor(agents): fold profile/runtime/tool catalog into settings/

Move the remaining config-surface helpers under settings/ and drop the
skill_package id-list aliases in favor of dump/parse_id_list.

Co-authored-by: Cursor <cursoragent@cursor.com>

* refactor(agents): group workspace_dir and execute_env under workspace/

Keep path resolution and harness execute-env injection together as one
workspace package; leave only manager and conversation_mode at top level.

Co-authored-by: Cursor <cursoragent@cursor.com>

* refactor(infra): fix history/SkillHub ownership and drop thin providers/

Move history_projection under history/, share SkillHub host/limits and
route expert skill downloads through skills.skillhub_market, and fold
Codex OAuth helpers into agents.providers to remove the tiny providers/
package.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: sync AGENTS.md and guides with infra/agents layout

Reflect subpackages (settings/workspace/threads/persona, history/, skills/)
and drop stale runtime.py / mbti_profiles paths after the agents reorg.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-25 22:32:41 +08:00

94 lines
2.9 KiB
Python

"""Unit tests for Langfuse settings store."""
from __future__ import annotations
import json
import urllib.request
from pathlib import Path
from unittest import mock
import pytest
from octop.infra.agents.settings.langfuse import LangfuseSettingsStore, verify_langfuse_credentials
from octop.infra.db.migrate import run_migrations
from octop.infra.db.pool import SqlitePool
from octop.infra.db.repos.secrets import SecretRepo
from octop.infra.db.repos.settings import SettingsRepo
from octop.infra.errors import OctopError
@pytest.fixture
def store(tmp_path: Path) -> LangfuseSettingsStore:
db = SqlitePool(tmp_path / "octop.db")
run_migrations(db)
return LangfuseSettingsStore(settings_repo=SettingsRepo(db), secret_repo=SecretRepo(db))
def test_langfuse_settings_roundtrip(store: LangfuseSettingsStore) -> None:
cfg = store.save(
enabled=True,
public_key="pk-test",
host="http://langfuse.example",
secret_key="sk-test",
)
assert cfg.enabled is True
assert cfg.public_key == "pk-test"
assert cfg.host == "http://langfuse.example"
assert cfg.secret_key_set is True
loaded = store.load()
assert loaded.enabled is True
assert loaded.public_key == "pk-test"
assert loaded.configured is True
def test_langfuse_requires_secret_when_enabling(store: LangfuseSettingsStore) -> None:
with pytest.raises(OctopError):
store.save(enabled=True, public_key="pk", host="http://x")
def test_langfuse_harness_config(store: LangfuseSettingsStore) -> None:
from octop_harness.observability.langfuse import LangfuseConfig
assert store.harness_config() == LangfuseConfig(enabled=False)
store.save(
enabled=True,
public_key="pk-test",
host="http://langfuse.example",
secret_key="sk-test",
)
hc = store.harness_config()
assert isinstance(hc, LangfuseConfig)
assert hc.enabled is True
assert hc.public_key == "pk-test"
assert hc.host == "http://langfuse.example"
assert hc.secret_key == "sk-test"
def test_verify_langfuse_credentials_accepts_legacy_project_shape() -> None:
payload = json.dumps({"data": [{"id": "proj-1", "name": "octop", "metadata": {}}]}).encode()
class _Resp:
status = 200
def read(self) -> bytes:
return payload
def __enter__(self) -> _Resp:
return self
def __exit__(self, *args: object) -> None:
return None
def fake_urlopen(req: urllib.request.Request, timeout: float = 15) -> _Resp:
assert req.get_method() == "GET"
assert req.full_url.endswith("/api/public/projects")
auth = req.headers["Authorization"]
assert auth.startswith("Basic ")
return _Resp()
with mock.patch("urllib.request.urlopen", fake_urlopen):
result = verify_langfuse_credentials("http://langfuse.example", "pk", "sk")
assert result == {"ok": True}