test_internal_runtime.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. from __future__ import annotations
  2. from pathlib import Path
  3. from typing import Any
  4. import pytest
  5. from pydantic import SecretStr
  6. from script_build_host.agents.model_manifest import resolve_script_role_model_manifest
  7. from script_build_host.domain.records import Principal
  8. from script_build_host.infrastructure.config import ScriptBuildSettings
  9. from script_build_host.internal_runtime import (
  10. InternalBuildAuthorizer,
  11. InternalPrincipalProvider,
  12. create_internal_runtime,
  13. )
  14. def _settings(tmp_path: Path, **overrides: Any) -> ScriptBuildSettings:
  15. values: dict[str, Any] = {
  16. "environment": "test",
  17. "read_database_url": f"sqlite+aiosqlite:///{tmp_path / 'read.db'}",
  18. "write_database_url": f"sqlite+aiosqlite:///{tmp_path / 'write.db'}",
  19. "agent_data_root": tmp_path / "agent-data",
  20. "persona_root": tmp_path / "persona",
  21. "section_pattern_root": tmp_path / "sections",
  22. "decode_index_root": tmp_path / "decode",
  23. "knowledge_root": tmp_path / "knowledge.json",
  24. "prompt_fallback_root": tmp_path / "prompts",
  25. "role_model": "qwen3.7-max",
  26. "embedding_api_key": SecretStr("secret"),
  27. }
  28. values.update(overrides)
  29. return ScriptBuildSettings(**values)
  30. def test_role_model_manifest_covers_all_sixteen_presets_and_keeps_overrides() -> None:
  31. result = resolve_script_role_model_manifest(
  32. {
  33. "embedding_model": "text-embedding-v4",
  34. "presets": {"script_root_validator": {"temperature": 0.1}},
  35. },
  36. model="qwen3.7-max",
  37. max_iterations=120,
  38. )
  39. presets = result["presets"]
  40. assert isinstance(presets, dict)
  41. assert len(presets) == 16
  42. assert {value["model"] for value in presets.values()} == {"qwen3.7-max"}
  43. assert presets["script_root_validator"]["temperature"] == 0.1
  44. assert presets["script_direction_worker"]["temperature"] == 0.2
  45. assert result["embedding_model"] == "text-embedding-v4"
  46. @pytest.mark.asyncio
  47. async def test_internal_auth_is_fixed_and_rejected_for_invalid_builds() -> None:
  48. provider = InternalPrincipalProvider(" internal-e2e ")
  49. principal = await provider.current(object())
  50. assert principal == Principal("internal-e2e")
  51. authorizer = InternalBuildAuthorizer()
  52. await authorizer.require_source_access(principal, execution_id=1, topic_build_id=2, topic_id=3)
  53. await authorizer.require_access(principal, 1)
  54. with pytest.raises(PermissionError):
  55. await authorizer.require_access(principal, 0)
  56. def test_internal_runtime_uses_qwen_and_fails_closed_outside_test(
  57. tmp_path: Path, monkeypatch: pytest.MonkeyPatch
  58. ) -> None:
  59. received: dict[str, Any] = {}
  60. def fake_qwen(**kwargs: Any) -> Any:
  61. received.update(kwargs)
  62. async def call(**_call: Any) -> dict[str, Any]:
  63. return {"content": "unused", "finish_reason": "stop"}
  64. return call
  65. monkeypatch.setattr("script_build_host.internal_runtime.create_qwen_llm_call", fake_qwen)
  66. settings = _settings(tmp_path)
  67. runtime = create_internal_runtime(settings)
  68. assert runtime.runner.trace_store is not None
  69. assert received == {
  70. "model": "qwen3.7-max",
  71. "base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1",
  72. "api_key": "secret",
  73. "enable_thinking": False,
  74. "request_timeout_seconds": 300.0,
  75. "max_retries": 2,
  76. }
  77. with pytest.raises(RuntimeError, match="only in test"):
  78. create_internal_runtime(
  79. _settings(
  80. tmp_path,
  81. environment="production",
  82. read_database_url="mysql+asyncmy://reader:r@db.example/app",
  83. read_database_socks_proxy=None,
  84. write_database_url="mysql+asyncmy://runtime:r@db.example/app",
  85. final_database_url="mysql+asyncmy://publisher:r@db.example/app",
  86. )
  87. )