mirror of
https://github.com/hanzoai/insights.git
synced 2026-10-30 16:15:31 +00:00
.stamphog/ was a config directory at the repo root — policy.yml, review-guidance.md — read by the PR review agent. It is .stamp/ now, and the 34 files that name it follow: the workflows, tools/pr-approval-agent, tach.toml, the sandbox Dockerfile, bin/debrand. One behaviour change worth naming rather than burying: the agent finds its own previous PR comment by an HTML marker, `<!-- stamphog:v1 ... -->`, and that marker is now `<!-- stamp:v1 ... -->`. Open PRs will get one new comment instead of an edit, once, and then match again. insights/test/setup_receivers_baseline.txt records registered signal receivers by module path, so it names products.review.backend now. stamphog: zero occurrences anywhere in the tree. review_hog: only inside migration files, whose names are their identity in django_migrations. Co-authored-by: Hanzo Dev <[email protected]>
128 lines
4.7 KiB
Python
128 lines
4.7 KiB
Python
"""Tests for ai-gateway routing in gateway.py."""
|
|
|
|
import pytest
|
|
|
|
from gateway import AI_PRODUCT, analytics_extra_properties, gateway_env, resolve_gateway_config
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _clear_gateway_env(monkeypatch):
|
|
monkeypatch.delenv("AI_GATEWAY_URL", raising=False)
|
|
monkeypatch.delenv("AI_GATEWAY_API_KEY", raising=False)
|
|
|
|
|
|
def test_unset_returns_none_direct_path(monkeypatch):
|
|
assert resolve_gateway_config() is None
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"url,api_key",
|
|
[
|
|
pytest.param("https://gateway.us.hanzo.ai/v1", "", id="url-only"),
|
|
pytest.param("", "sk-secret", id="key-only"),
|
|
],
|
|
)
|
|
def test_half_applied_config_falls_back(monkeypatch, url, api_key):
|
|
monkeypatch.setenv("AI_GATEWAY_URL", url)
|
|
monkeypatch.setenv("AI_GATEWAY_API_KEY", api_key)
|
|
assert resolve_gateway_config() is None
|
|
|
|
|
|
def test_url_without_v1_falls_back(monkeypatch):
|
|
monkeypatch.setenv("AI_GATEWAY_URL", "https://gateway.us.hanzo.ai")
|
|
monkeypatch.setenv("AI_GATEWAY_API_KEY", "sk-secret")
|
|
assert resolve_gateway_config() is None
|
|
|
|
|
|
def test_schemeless_url_falls_back(monkeypatch):
|
|
# Schemeless parses all into .path, so the /v1 check alone would pass.
|
|
monkeypatch.setenv("AI_GATEWAY_URL", "gateway.us.hanzo.ai/v1")
|
|
monkeypatch.setenv("AI_GATEWAY_API_KEY", "sk-secret")
|
|
assert resolve_gateway_config() is None
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"bad_url",
|
|
[
|
|
pytest.param("https://gateway.us.hanzo.ai/v1?x=y", id="query"),
|
|
pytest.param("https://gateway.us.hanzo.ai/v1#frag", id="fragment"),
|
|
],
|
|
)
|
|
def test_url_with_query_or_fragment_falls_back(monkeypatch, bad_url):
|
|
# A query/fragment survives /v1 stripping and would corrupt the base URL.
|
|
monkeypatch.setenv("AI_GATEWAY_URL", bad_url)
|
|
monkeypatch.setenv("AI_GATEWAY_API_KEY", "sk-secret")
|
|
assert resolve_gateway_config() is None
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"configured_url,expected_base",
|
|
[
|
|
pytest.param("https://gateway.us.hanzo.ai/v1", "https://gateway.us.hanzo.ai", id="strips-v1"),
|
|
pytest.param(
|
|
"https://gateway.us.hanzo.ai/v1/", "https://gateway.us.hanzo.ai", id="strips-v1-trailing-slash"
|
|
),
|
|
],
|
|
)
|
|
def test_resolves_and_strips_v1_for_anthropic_base(monkeypatch, configured_url, expected_base):
|
|
# SDK re-appends /v1/messages, so resolve() returns the bare host.
|
|
monkeypatch.setenv("AI_GATEWAY_URL", configured_url)
|
|
monkeypatch.setenv("AI_GATEWAY_API_KEY", "sk-secret")
|
|
assert resolve_gateway_config() == (expected_base, "sk-secret")
|
|
|
|
|
|
def test_gateway_env_points_sdk_at_gateway():
|
|
env = gateway_env("https://gateway.us.hanzo.ai", "sk-secret", {"stamp_pr_number": 123})
|
|
assert env["ANTHROPIC_BASE_URL"] == "https://gateway.us.hanzo.ai"
|
|
assert env["ANTHROPIC_AUTH_TOKEN"] == "sk-secret"
|
|
assert env["ANTHROPIC_API_KEY"] == "sk-secret"
|
|
|
|
|
|
def test_gateway_env_tags_ai_product_and_attribution():
|
|
# Go gateway reads one X-Insights-Properties JSON blob, not x-insights-property-*.
|
|
env = gateway_env("https://host", "sk-secret", {"stamp_pr_number": 123, "stamp_repo": "Insights/insights"})
|
|
headers = env["ANTHROPIC_CUSTOM_HEADERS"]
|
|
assert headers == (
|
|
'X-Insights-Properties: {"ai_product":"aio_stamp","stamp_pr_number":123,"stamp_repo":"Insights/insights"}'
|
|
)
|
|
assert "x-insights-property-" not in headers
|
|
|
|
|
|
def test_ai_product_uses_aio_prefix_no_reserved_prefix():
|
|
assert AI_PRODUCT == "aio_stamp"
|
|
assert not AI_PRODUCT.startswith("$")
|
|
|
|
|
|
def test_header_values_are_single_line():
|
|
env = gateway_env("https://host", "sk-secret", {"stamp_pr_title": "line one\nline two"})
|
|
header = env["ANTHROPIC_CUSTOM_HEADERS"]
|
|
assert "\n" not in header
|
|
assert '"stamp_pr_title":"line one line two"' in header
|
|
|
|
|
|
def test_none_attribution_values_dropped():
|
|
env = gateway_env("https://host", "sk-secret", {"stamp_commit_type": None})
|
|
assert "stamp_commit_type" not in env["ANTHROPIC_CUSTOM_HEADERS"]
|
|
|
|
|
|
def test_extra_properties_parses_server_injected_json(monkeypatch):
|
|
monkeypatch.setenv("STAMPFN_EXTRA_PROPERTIES", '{"stamp_runtime":"hosted","stamp_team_id":2}')
|
|
assert analytics_extra_properties() == {"stamp_runtime": "hosted", "stamp_team_id": 2}
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"raw",
|
|
[
|
|
pytest.param(None, id="absent-action-path"),
|
|
pytest.param("{not json", id="malformed-json"),
|
|
pytest.param('["not", "a", "dict"]', id="non-dict-json"),
|
|
],
|
|
)
|
|
def test_extra_properties_degrade_to_empty(monkeypatch, raw):
|
|
# Telemetry must never fail a review: anything but a JSON object degrades to {}.
|
|
if raw is None:
|
|
monkeypatch.delenv("STAMPFN_EXTRA_PROPERTIES", raising=False)
|
|
else:
|
|
monkeypatch.setenv("STAMPFN_EXTRA_PROPERTIES", raw)
|
|
assert analytics_extra_properties() == {}
|