4b6817381b
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
CI / coverage-report (push) Has been cancelled
CI / test-kubernetes (push) Has been cancelled
CI / should-run-thorough (push) Has been cancelled
CI / test-thorough (cloudwatch-demo) (push) Has been cancelled
CI / test-thorough (flink-ecs) (push) Has been cancelled
CI / test-thorough (upstream-lambda) (push) Has been cancelled
CI / test-thorough (prefect-ecs-fargate) (push) Has been cancelled
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Has been cancelled
Benchmark image — build + push to ECR (any adapter) / build + push (push) Has been cancelled
CI / quality (ubuntu-latest) (push) Has been cancelled
CI / test (tools-runtime) (push) Has been cancelled
CI / test (e2e-general) (push) Has been cancelled
CI / test (cli-runtime) (push) Has been cancelled
CI / test (e2e-provider-and-openclaw) (push) Has been cancelled
CI / test (integrations-and-misc) (push) Has been cancelled
Release / verify (push) Has been cancelled
Release / build-python-dist (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Has been cancelled
Release / publish-release (push) Has been cancelled
Release / publish-main-release (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Has been cancelled
Release / prepare (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Has been cancelled
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Has been cancelled
211 lines
8.5 KiB
Python
211 lines
8.5 KiB
Python
"""Unit tests for the VictoriaLogs integration.
|
|
|
|
Covers:
|
|
- ``VictoriaLogsIntegrationConfig`` model (base_url + tenant_id normalization)
|
|
- ``load_env_integrations`` env-var parsing
|
|
- ``_classify_service_instance`` DB-store classification
|
|
- ``_verify_victoria_logs`` (missing / failed / passed paths)
|
|
|
|
The verifier is built via ``build_probe_verifier`` and ultimately delegates
|
|
to ``VictoriaLogsClient.probe_access()``, which must return ``status='passed'``
|
|
on success — ``verification_exit_code()`` checks for that string specifically.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import httpx
|
|
import pytest
|
|
|
|
from integrations._catalog_impl import _classify_service_instance
|
|
from integrations.catalog import load_env_integrations
|
|
from integrations.config_models import VictoriaLogsIntegrationConfig
|
|
from integrations.victoria_logs.verifier import verify_victoria_logs as _verify_victoria_logs
|
|
|
|
|
|
class TestVictoriaLogsIntegrationConfig:
|
|
"""Tests for the Pydantic config model."""
|
|
|
|
def test_base_url_strip_and_rstrip_slash(self) -> None:
|
|
config = VictoriaLogsIntegrationConfig(base_url=" http://vmlogs:9428/ ")
|
|
assert config.base_url == "http://vmlogs:9428"
|
|
|
|
def test_tenant_id_unset_normalizes_to_none(self) -> None:
|
|
config = VictoriaLogsIntegrationConfig(base_url="http://vmlogs:9428")
|
|
assert config.tenant_id is None
|
|
|
|
def test_tenant_id_empty_string_normalizes_to_none(self) -> None:
|
|
config = VictoriaLogsIntegrationConfig(base_url="http://vmlogs:9428", tenant_id="")
|
|
assert config.tenant_id is None
|
|
|
|
def test_tenant_id_whitespace_normalizes_to_none(self) -> None:
|
|
config = VictoriaLogsIntegrationConfig(base_url="http://vmlogs:9428", tenant_id=" ")
|
|
assert config.tenant_id is None
|
|
|
|
def test_tenant_id_explicit_value_preserved(self) -> None:
|
|
config = VictoriaLogsIntegrationConfig(base_url="http://vmlogs:9428", tenant_id="acme")
|
|
assert config.tenant_id == "acme"
|
|
|
|
def test_integration_id_default_empty(self) -> None:
|
|
config = VictoriaLogsIntegrationConfig(base_url="http://vmlogs:9428")
|
|
assert config.integration_id == ""
|
|
|
|
|
|
class TestLoadEnvIntegrations:
|
|
"""Tests for VictoriaLogs env-var loading in load_env_integrations."""
|
|
|
|
def test_url_alone_loads_active_integration(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setenv("VICTORIA_LOGS_URL", "http://vmlogs:9428")
|
|
monkeypatch.delenv("VICTORIA_LOGS_TENANT_ID", raising=False)
|
|
records = load_env_integrations()
|
|
|
|
victoria = next((r for r in records if r["service"] == "victoria_logs"), None)
|
|
assert victoria is not None
|
|
assert victoria["status"] == "active"
|
|
assert victoria["credentials"]["base_url"] == "http://vmlogs:9428"
|
|
assert victoria["credentials"]["tenant_id"] is None
|
|
|
|
def test_no_url_means_no_record(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.delenv("VICTORIA_LOGS_URL", raising=False)
|
|
records = load_env_integrations()
|
|
|
|
assert all(r["service"] != "victoria_logs" for r in records)
|
|
|
|
def test_tenant_id_propagates_when_set(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setenv("VICTORIA_LOGS_URL", "http://vmlogs:9428")
|
|
monkeypatch.setenv("VICTORIA_LOGS_TENANT_ID", "acme")
|
|
records = load_env_integrations()
|
|
|
|
victoria = next((r for r in records if r["service"] == "victoria_logs"), None)
|
|
assert victoria is not None
|
|
assert victoria["credentials"]["tenant_id"] == "acme"
|
|
|
|
|
|
class TestClassifyServiceInstance:
|
|
"""Tests for _classify_service_instance — the DB-store classification path."""
|
|
|
|
def test_classifies_victoria_logs_with_base_url(self) -> None:
|
|
config, key = _classify_service_instance(
|
|
key="victoria_logs",
|
|
credentials={"base_url": "http://vmlogs:9428"},
|
|
record_id="vl-prod",
|
|
)
|
|
assert key == "victoria_logs"
|
|
assert config is not None
|
|
assert config.base_url == "http://vmlogs:9428"
|
|
assert config.integration_id == "vl-prod"
|
|
|
|
def test_skips_when_base_url_missing(self) -> None:
|
|
config, key = _classify_service_instance(
|
|
key="victoria_logs",
|
|
credentials={"base_url": ""},
|
|
record_id="vl-broken",
|
|
)
|
|
assert config is None and key is None
|
|
|
|
def test_classifies_with_tenant_id(self) -> None:
|
|
config, key = _classify_service_instance(
|
|
key="victoria_logs",
|
|
credentials={"base_url": "http://vmlogs:9428", "tenant_id": "team-a"},
|
|
record_id="vl-team-a",
|
|
)
|
|
assert key == "victoria_logs"
|
|
assert config is not None
|
|
assert config.tenant_id == "team-a"
|
|
|
|
|
|
class TestVerifyVictoriaLogs:
|
|
"""Tests for the verifier — focuses on the status contract."""
|
|
|
|
def test_missing_when_base_url_absent(self) -> None:
|
|
result = _verify_victoria_logs(source="local env", config={"base_url": ""})
|
|
assert result["status"] == "missing"
|
|
assert result["service"] == "victoria_logs"
|
|
|
|
def test_passed_on_successful_query(self) -> None:
|
|
mock_response = MagicMock()
|
|
mock_response.status_code = 200
|
|
mock_response.text = '{"_msg":"hello"}\n'
|
|
mock_response.raise_for_status.return_value = None
|
|
|
|
with patch("httpx.Client.get", return_value=mock_response):
|
|
result = _verify_victoria_logs(
|
|
source="local env",
|
|
config={"base_url": "http://vmlogs:9428"},
|
|
)
|
|
|
|
assert result["status"] == "passed", (
|
|
"Verifier must return status='passed' on success — "
|
|
"verification_exit_code() checks for 'passed' specifically."
|
|
)
|
|
assert "vmlogs:9428" in result["detail"]
|
|
|
|
def test_failed_on_http_error(self) -> None:
|
|
mock_response = MagicMock()
|
|
mock_response.status_code = 500
|
|
mock_response.text = "internal error"
|
|
mock_response.raise_for_status.side_effect = httpx.HTTPStatusError(
|
|
"500 Server Error",
|
|
request=MagicMock(),
|
|
response=mock_response,
|
|
)
|
|
|
|
with patch("httpx.Client.get", return_value=mock_response):
|
|
result = _verify_victoria_logs(
|
|
source="local env",
|
|
config={"base_url": "http://vmlogs:9428"},
|
|
)
|
|
|
|
assert result["status"] == "failed"
|
|
assert "500" in result["detail"]
|
|
|
|
|
|
class TestVictoriaLogsIntegrationCanonicalShape:
|
|
"""Sanity checks that the integration shows up where the runtime expects it."""
|
|
|
|
def test_classified_config_dump_matches_pydantic_model(self) -> None:
|
|
"""The classifier returns a typed model that round-trips through model_validate."""
|
|
config, _ = _classify_service_instance(
|
|
key="victoria_logs",
|
|
credentials={"base_url": "http://vmlogs:9428", "tenant_id": "x"},
|
|
record_id="vl-1",
|
|
)
|
|
assert isinstance(config, VictoriaLogsIntegrationConfig)
|
|
roundtrip = VictoriaLogsIntegrationConfig.model_validate(
|
|
config.model_dump(exclude_none=True)
|
|
)
|
|
assert roundtrip.base_url == "http://vmlogs:9428"
|
|
assert roundtrip.tenant_id == "x"
|
|
|
|
def test_effective_integrations_field_exists(self) -> None:
|
|
"""``victoria_logs`` must be a declared field on EffectiveIntegrations."""
|
|
from integrations.effective_models import (
|
|
EffectiveIntegrationEntry,
|
|
EffectiveIntegrations,
|
|
)
|
|
|
|
entry = EffectiveIntegrationEntry(
|
|
source="local env",
|
|
config={"base_url": "http://vmlogs:9428"},
|
|
)
|
|
# Should not raise (EffectiveIntegrations uses extra='forbid').
|
|
effective: dict[str, Any] = {"victoria_logs": entry.model_dump()}
|
|
EffectiveIntegrations.model_validate(effective)
|
|
|
|
def test_registry_spec_present(self) -> None:
|
|
"""Confirm the registry spec is wired so SERVICE_KEY_MAP and verifier dispatch work."""
|
|
from integrations.registry import INTEGRATION_SPECS, SERVICE_KEY_MAP
|
|
|
|
spec = next((s for s in INTEGRATION_SPECS if s.service == "victoria_logs"), None)
|
|
assert spec is not None
|
|
assert spec.direct_effective is True
|
|
assert spec.has_verifier
|
|
from integrations.verification import get_verifier
|
|
|
|
assert get_verifier("victoria_logs") is _verify_victoria_logs
|
|
# Alias map: both spellings normalize to the canonical service.
|
|
assert SERVICE_KEY_MAP["victoria_logs"] == "victoria_logs"
|
|
assert SERVICE_KEY_MAP["victorialogs"] == "victoria_logs"
|