Files
computer-playbook/tests/unit/python/roles/agents/test_openclaw_model_provider.py
T
kevinveenbirkenbachandClaude Opus 5.5 3610e7b9ef fix(code-scanning): resolve the alerts that describe real code
Of the 76 open code scanning alerts, 39 point at code that can change.

svc-ai-mcp-adapter:
- A path argument that is empty or made only of dots is refused.
  Percent-encoding keeps a separator inside its segment, but "." and ".."
  need none, so an upstream that resolves dot segments could answer from a
  path the contract does not name.
- parse_duration splits on the units. The pattern it used before rescanned
  a long run of digits from every start position.

Tests and the README generators:
- Every Jinja environment built with autoescape=False and a "noqa: S701"
  uses select_autoescape now, as six test modules and
  utils/templating/jinja.py already did. It escapes templates named
  *.html, *.htm and *.xml, which none of these call sites loads.
- Dead code goes: a doubled "rel" block in the email lint, an unread
  _SKIP_DIRS, and a module that _resolve returned to no caller.
- Two "except ...: pass" blocks become contextlib.suppress and
  assertRaises.
- Nine test modules import unittest.mock one way instead of two, and one
  lookup test imports its module once.

Closes #807

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 09:18:00 +02:00

96 lines
3.2 KiB
Python

"""OpenClaw refuses to boot on a model entry that misses a key.
The gateway validates its whole configuration before it serves anything, and
a rejected file is not a degraded start but a crash loop: OpenClaw retries,
trips its own restart breaker after four unclean boots, and the container
exits 128 while the deploy sits in the health check until it gives up.
The provider block reached the repository without ever being rendered in a
test - every case here passed OPENCLAW_LITELLM_ENABLED=False, so the block was
skipped and its shape was never checked against anything.
"""
from __future__ import annotations
import importlib.util
import json
import unittest
from jinja2 import Environment, FileSystemLoader, select_autoescape
from . import PROJECT_ROOT
OPENCLAW = PROJECT_ROOT / "roles/web-app-openclaw/templates"
FILTERS = PROJECT_ROOT / "plugins/filter/mcp/authorization.py"
MODEL = "qwen2.5:0.5b"
CONTEXT = {
"OPENCLAW_URL": "https://claw.example.org",
"OPENCLAW_LITELLM_ENABLED": True,
"OPENCLAW_LITELLM_PROVIDER": "litellm",
"OPENCLAW_LITELLM_MODEL": MODEL,
"OPENCLAW_LITELLM_BASE_URL": "http://litellm:4000/v1",
"OPENCLAW_LITELLM_API_KEY": "sk-" + "x" * 20,
"OPENCLAW_MCP_SERVERS": [],
}
def _filters() -> dict:
spec = importlib.util.spec_from_file_location("mcp_authorization", FILTERS)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
filters = dict(module.FilterModule().filters())
filters.update({"to_json": json.dumps, "bool": bool})
return filters
def _render(**overrides) -> dict:
"""Return the gateway config as OpenClaw would parse it.
Args:
overrides: context values replacing the defaults.
"""
env = Environment(
loader=FileSystemLoader(str(OPENCLAW)), autoescape=select_autoescape()
)
env.filters.update(_filters())
return json.loads(
env.get_template("openclaw.json.j2").render(**{**CONTEXT, **overrides})
)
class TestOpenClawModelProvider(unittest.TestCase):
def _entry(self, **overrides) -> dict:
"""Return the provider's single model entry.
Args:
overrides: context values replacing the defaults.
"""
return _render(**overrides)["models"]["providers"]["litellm"]["models"][0]
def test_the_model_entry_carries_both_keys_the_gateway_validates(self) -> None:
self.assertEqual({"id": MODEL, "name": MODEL}, self._entry())
def test_the_name_matches_the_id(self) -> None:
"""Which key reaches the API is the upstream's choice, not ours."""
entry = self._entry()
self.assertEqual(entry["id"], entry["name"])
def test_the_primary_agent_model_names_the_same_provider(self) -> None:
rendered = _render()
self.assertEqual(
f"litellm/{MODEL}",
rendered["agents"]["defaults"]["model"]["primary"],
)
self.assertIn("litellm", rendered["models"]["providers"])
def test_the_block_disappears_when_litellm_is_off(self) -> None:
rendered = _render(OPENCLAW_LITELLM_ENABLED=False)
self.assertNotIn("models", rendered)
self.assertNotIn("agents", rendered)
if __name__ == "__main__":
unittest.main()