4b6817381b
Benchmark image — build + push to ECR (any adapter) / build + push (push) Waiting to run
CI / quality (ubuntu-latest) (push) Waiting to run
CI / test (tools-runtime) (push) Waiting to run
CI / test (e2e-general) (push) Waiting to run
CI / test (cli-runtime) (push) Waiting to run
CI / test (e2e-provider-and-openclaw) (push) Waiting to run
CI / test (integrations-and-misc) (push) Waiting to run
CI / coverage-report (push) Blocked by required conditions
CI / test-kubernetes (push) Waiting to run
CI / should-run-thorough (push) Waiting to run
CI / test-thorough (cloudwatch-demo) (push) Blocked by required conditions
CI / test-thorough (flink-ecs) (push) Blocked by required conditions
CI / test-thorough (upstream-lambda) (push) Blocked by required conditions
CI / test-thorough (prefect-ecs-fargate) (push) Blocked by required conditions
CodeQL / Analyze (python) (push) Waiting to run
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Blocked by required conditions
Release / publish-release (push) Blocked by required conditions
Release / publish-main-release (push) Blocked by required conditions
Release / prepare (push) Waiting to run
Release / verify (push) Blocked by required conditions
Release / build-python-dist (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Blocked by required conditions
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Waiting to run
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Waiting to run
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Waiting to run
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
653 lines
19 KiB
Python
653 lines
19 KiB
Python
"""Tests for shared gitlab integration helpers."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
import httpx
|
|
import pytest
|
|
from pydantic import ValidationError
|
|
|
|
from integrations.gitlab import (
|
|
DEFAULT_GITLAB_BASE_URL,
|
|
GitlabConfig,
|
|
build_gitlab_config,
|
|
get_gitlab_commits,
|
|
get_gitlab_file,
|
|
get_gitlab_mrs,
|
|
get_gitlab_pipelines,
|
|
gitlab_config_from_env,
|
|
post_gitlab_mr_note,
|
|
validate_gitlab_config,
|
|
validate_gitlab_connection,
|
|
)
|
|
from integrations.gitlab.verifier import verify_gitlab
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helpers
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class _FakeResponse:
|
|
def __init__(self, payload: Any, status_code: int = 200) -> None:
|
|
self._payload = payload
|
|
self.status_code = status_code
|
|
self.text = ""
|
|
|
|
def raise_for_status(self) -> None:
|
|
if self.status_code >= 400:
|
|
raise httpx.HTTPStatusError(
|
|
"error",
|
|
request=httpx.Request("GET", "https://gitlab.com"),
|
|
response=httpx.Response(self.status_code),
|
|
)
|
|
|
|
def json(self) -> Any:
|
|
return self._payload
|
|
|
|
|
|
def _make_config(**kwargs: Any) -> GitlabConfig:
|
|
return GitlabConfig.model_validate({"auth_token": "test-token", **kwargs})
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# GitlabConfig
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_gitlab_config_defaults() -> None:
|
|
config = _make_config()
|
|
|
|
assert config.base_url == DEFAULT_GITLAB_BASE_URL
|
|
assert config.timeout_seconds == 15.0
|
|
|
|
|
|
def test_gitlab_config_api_base_url_strips_trailing_slash() -> None:
|
|
config = _make_config(base_url="https://gitlab.example.com/api/v4/")
|
|
|
|
assert config.api_base_url == "https://gitlab.example.com/api/v4"
|
|
|
|
|
|
def test_gitlab_config_auth_headers_include_bearer_token() -> None:
|
|
config = _make_config(auth_token="my-secret-token")
|
|
|
|
assert config.auth_headers == {
|
|
"Authorization": "Bearer my-secret-token",
|
|
"Accept": "application/json",
|
|
}
|
|
|
|
|
|
def test_gitlab_config_normalizes_empty_base_url_to_default() -> None:
|
|
config = _make_config(base_url="")
|
|
|
|
assert config.base_url == DEFAULT_GITLAB_BASE_URL
|
|
|
|
|
|
def test_gitlab_config_normalizes_none_base_url_to_default() -> None:
|
|
config = GitlabConfig.model_validate({"auth_token": "tok", "base_url": None})
|
|
|
|
assert config.base_url == DEFAULT_GITLAB_BASE_URL
|
|
|
|
|
|
def test_gitlab_config_rejects_non_positive_timeout() -> None:
|
|
with pytest.raises(ValidationError):
|
|
_make_config(timeout_seconds=0)
|
|
|
|
|
|
def test_gitlab_config_rejects_unknown_fields_with_suggestion() -> None:
|
|
with pytest.raises(ValidationError, match="auth_toke.*auth_token"):
|
|
GitlabConfig.model_validate({"auth_toke": "tok"})
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# build_gitlab_config / gitlab_config_from_env
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_build_gitlab_config_accepts_empty_dict() -> None:
|
|
config = build_gitlab_config({})
|
|
|
|
assert config.base_url == DEFAULT_GITLAB_BASE_URL
|
|
assert config.auth_token == ""
|
|
|
|
|
|
def test_build_gitlab_config_accepts_none() -> None:
|
|
config = build_gitlab_config(None)
|
|
|
|
assert config.base_url == DEFAULT_GITLAB_BASE_URL
|
|
|
|
|
|
def test_gitlab_config_from_env_returns_none_when_token_missing(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
monkeypatch.delenv("GITLAB_ACCESS_TOKEN", raising=False)
|
|
|
|
assert gitlab_config_from_env() is None
|
|
|
|
|
|
def test_gitlab_config_from_env_builds_config_from_env(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
monkeypatch.setenv("GITLAB_ACCESS_TOKEN", "gl-token")
|
|
monkeypatch.setenv("GITLAB_BASE_URL", "https://gitlab.example.com/api/v4")
|
|
|
|
config = gitlab_config_from_env()
|
|
|
|
assert config is not None
|
|
assert config.auth_token == "gl-token"
|
|
assert config.base_url == "https://gitlab.example.com/api/v4"
|
|
|
|
|
|
def test_gitlab_config_from_env_uses_default_url_when_not_set(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
monkeypatch.setenv("GITLAB_ACCESS_TOKEN", "gl-token")
|
|
monkeypatch.delenv("GITLAB_BASE_URL", raising=False)
|
|
|
|
config = gitlab_config_from_env()
|
|
|
|
assert config is not None
|
|
assert config.base_url == DEFAULT_GITLAB_BASE_URL
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# validate_gitlab_config
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_validate_gitlab_config_fails_when_token_missing() -> None:
|
|
config = build_gitlab_config({})
|
|
|
|
result = validate_gitlab_config(config)
|
|
|
|
assert result.ok is False
|
|
assert "auth token is required" in result.detail
|
|
|
|
|
|
def test_validate_gitlab_config_returns_ok_on_success(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
def _ok(*, config: GitlabConfig) -> dict[str, str]:
|
|
del config
|
|
return {"username": "gl-user"}
|
|
|
|
monkeypatch.setattr(
|
|
"integrations.gitlab.validate_gitlab_connection",
|
|
_ok,
|
|
)
|
|
|
|
result = validate_gitlab_config(_make_config())
|
|
|
|
assert result.ok is True
|
|
assert "@gl-user" in result.detail
|
|
|
|
|
|
def test_validate_gitlab_config_handles_http_error(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
def _raise(*, _config: GitlabConfig) -> None:
|
|
response = httpx.Response(401)
|
|
response._content = b"Unauthorized"
|
|
raise httpx.HTTPStatusError(
|
|
"401",
|
|
request=httpx.Request("GET", "https://gitlab.com/api/v4/user"),
|
|
response=response,
|
|
)
|
|
|
|
monkeypatch.setattr("integrations.gitlab.validate_gitlab_connection", _raise)
|
|
|
|
result = validate_gitlab_config(_make_config())
|
|
|
|
assert result.ok is False
|
|
assert "validation failed" in result.detail
|
|
|
|
|
|
def test_validate_gitlab_config_handles_generic_exception(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
def _boom(*, config: GitlabConfig) -> dict[str, str]:
|
|
del config
|
|
raise RuntimeError("connection refused")
|
|
|
|
monkeypatch.setattr(
|
|
"integrations.gitlab.validate_gitlab_connection",
|
|
_boom,
|
|
)
|
|
|
|
result = validate_gitlab_config(_make_config())
|
|
|
|
assert result.ok is False
|
|
assert "connection refused" in result.detail
|
|
|
|
|
|
def test_verify_gitlab_wraps_validation_result(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
def _ok(*, config: GitlabConfig) -> dict[str, str]:
|
|
del config
|
|
return {"username": "gl-user"}
|
|
|
|
monkeypatch.setattr("integrations.gitlab.validate_gitlab_connection", _ok)
|
|
|
|
result = verify_gitlab("local store", {"auth_token": "test-token"})
|
|
|
|
assert result == {
|
|
"service": "gitlab",
|
|
"source": "local store",
|
|
"status": "passed",
|
|
"detail": "GitLab connectivity successful. Authenticated as @gl-user",
|
|
}
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# validate_gitlab_connection
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_validate_gitlab_connection_returns_user_dict(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
monkeypatch.setattr(
|
|
"integrations.gitlab.httpx.request",
|
|
lambda *_, **__: _FakeResponse({"id": 1, "username": "gl-user"}),
|
|
)
|
|
|
|
result = validate_gitlab_connection(config=_make_config())
|
|
|
|
assert result == {"id": 1, "username": "gl-user"}
|
|
|
|
|
|
def test_validate_gitlab_connection_returns_empty_dict_for_non_dict_response(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
monkeypatch.setattr(
|
|
"integrations.gitlab.httpx.request",
|
|
lambda *_, **__: _FakeResponse(["unexpected", "list"]),
|
|
)
|
|
|
|
result = validate_gitlab_connection(config=_make_config())
|
|
|
|
assert result == {}
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# get_gitlab_commits
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_get_gitlab_commits_returns_list(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
commits = [{"id": "abc123", "title": "Fix bug"}]
|
|
monkeypatch.setattr(
|
|
"integrations.gitlab.httpx.request",
|
|
lambda *_, **__: _FakeResponse(commits),
|
|
)
|
|
|
|
result = get_gitlab_commits(
|
|
config=_make_config(),
|
|
project_id="my-org/my-repo",
|
|
since="2024-01-01T00:00:00Z",
|
|
)
|
|
|
|
assert result == commits
|
|
|
|
|
|
def test_get_gitlab_commits_url_encodes_project_id(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
captured: dict[str, Any] = {}
|
|
|
|
def _fake_request(_method: str, url: str, **_kw: Any) -> _FakeResponse:
|
|
captured["url"] = url
|
|
return _FakeResponse([])
|
|
|
|
monkeypatch.setattr("integrations.gitlab.httpx.request", _fake_request)
|
|
|
|
get_gitlab_commits(
|
|
config=_make_config(),
|
|
project_id="my-org/my-repo",
|
|
since="2024-01-01T00:00:00Z",
|
|
)
|
|
|
|
assert "my-org%2Fmy-repo" in captured["url"]
|
|
|
|
|
|
def test_get_gitlab_commits_returns_empty_list_for_non_list_response(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
monkeypatch.setattr(
|
|
"integrations.gitlab.httpx.request",
|
|
lambda *_, **__: _FakeResponse({"error": "not found"}),
|
|
)
|
|
|
|
result = get_gitlab_commits(
|
|
config=_make_config(),
|
|
project_id="my-org/my-repo",
|
|
since="2024-01-01T00:00:00Z",
|
|
)
|
|
|
|
assert result == []
|
|
|
|
|
|
def test_get_gitlab_commits_includes_since_param_when_provided(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
captured: dict[str, Any] = {}
|
|
|
|
def _fake_request(_method: str, _url: str, **kw: Any) -> _FakeResponse:
|
|
captured["params"] = kw.get("params", [])
|
|
return _FakeResponse([])
|
|
|
|
monkeypatch.setattr("integrations.gitlab.httpx.request", _fake_request)
|
|
|
|
get_gitlab_commits(
|
|
config=_make_config(),
|
|
project_id="proj",
|
|
since="2024-06-01T00:00:00Z",
|
|
)
|
|
|
|
param_keys = [k for k, _ in captured["params"]]
|
|
assert "since" in param_keys
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# get_gitlab_mrs
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_get_gitlab_mrs_returns_list(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
mrs = [{"iid": 1, "title": "Add feature"}]
|
|
monkeypatch.setattr(
|
|
"integrations.gitlab.httpx.request",
|
|
lambda *_, **__: _FakeResponse(mrs),
|
|
)
|
|
|
|
result = get_gitlab_mrs(
|
|
config=_make_config(),
|
|
project_id="my-org/my-repo",
|
|
updated_after="2024-01-01T00:00:00Z",
|
|
)
|
|
|
|
assert result == mrs
|
|
|
|
|
|
def test_get_gitlab_mrs_url_encodes_project_id(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
captured: dict[str, Any] = {}
|
|
|
|
def _fake_request(_method: str, url: str, **_kw: Any) -> _FakeResponse:
|
|
captured["url"] = url
|
|
return _FakeResponse([])
|
|
|
|
monkeypatch.setattr("integrations.gitlab.httpx.request", _fake_request)
|
|
|
|
get_gitlab_mrs(
|
|
config=_make_config(),
|
|
project_id="my-org/my-repo",
|
|
updated_after="2024-01-01T00:00:00Z",
|
|
)
|
|
|
|
assert "my-org%2Fmy-repo" in captured["url"]
|
|
|
|
|
|
def test_get_gitlab_mrs_returns_empty_list_for_non_list_response(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
monkeypatch.setattr(
|
|
"integrations.gitlab.httpx.request",
|
|
lambda *_, **__: _FakeResponse({}),
|
|
)
|
|
|
|
result = get_gitlab_mrs(
|
|
config=_make_config(),
|
|
project_id="proj",
|
|
updated_after="2024-01-01T00:00:00Z",
|
|
)
|
|
|
|
assert result == []
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# get_gitlab_pipelines
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_get_gitlab_pipelines_returns_list(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
pipelines = [{"id": 99, "status": "failed"}]
|
|
monkeypatch.setattr(
|
|
"integrations.gitlab.httpx.request",
|
|
lambda *_, **__: _FakeResponse(pipelines),
|
|
)
|
|
|
|
result = get_gitlab_pipelines(
|
|
config=_make_config(),
|
|
project_id="my-org/my-repo",
|
|
updated_after="2024-01-01T00:00:00Z",
|
|
)
|
|
|
|
assert result == pipelines
|
|
|
|
|
|
def test_get_gitlab_pipelines_url_encodes_project_id(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
captured: dict[str, Any] = {}
|
|
|
|
def _fake_request(_method: str, url: str, **_kw: Any) -> _FakeResponse:
|
|
captured["url"] = url
|
|
return _FakeResponse([])
|
|
|
|
monkeypatch.setattr("integrations.gitlab.httpx.request", _fake_request)
|
|
|
|
get_gitlab_pipelines(
|
|
config=_make_config(),
|
|
project_id="my-org/my-repo",
|
|
updated_after="2024-01-01T00:00:00Z",
|
|
)
|
|
|
|
assert "my-org%2Fmy-repo" in captured["url"]
|
|
|
|
|
|
def test_get_gitlab_pipelines_returns_empty_list_for_non_list_response(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
monkeypatch.setattr(
|
|
"integrations.gitlab.httpx.request",
|
|
lambda *_, **__: _FakeResponse({"unexpected": "dict"}),
|
|
)
|
|
|
|
result = get_gitlab_pipelines(
|
|
config=_make_config(),
|
|
project_id="proj",
|
|
updated_after="2024-01-01T00:00:00Z",
|
|
)
|
|
|
|
assert result == []
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# get_gitlab_file
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_get_gitlab_file_returns_dict(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
file_data = {"file_name": "README.md", "content": "SGVsbG8="}
|
|
monkeypatch.setattr(
|
|
"integrations.gitlab.httpx.request",
|
|
lambda *_, **__: _FakeResponse(file_data),
|
|
)
|
|
|
|
result = get_gitlab_file(
|
|
config=_make_config(),
|
|
project_id="my-org/my-repo",
|
|
file_path="README.md",
|
|
)
|
|
|
|
assert result == file_data
|
|
|
|
|
|
def test_get_gitlab_file_url_encodes_project_id_and_path(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
captured: dict[str, Any] = {}
|
|
|
|
def _fake_request(_method: str, url: str, **_kw: Any) -> _FakeResponse:
|
|
captured["url"] = url
|
|
return _FakeResponse({})
|
|
|
|
monkeypatch.setattr("integrations.gitlab.httpx.request", _fake_request)
|
|
|
|
get_gitlab_file(
|
|
config=_make_config(),
|
|
project_id="my-org/my-repo",
|
|
file_path="src/main.py",
|
|
)
|
|
|
|
assert "my-org%2Fmy-repo" in captured["url"]
|
|
assert "src%2Fmain.py" in captured["url"]
|
|
|
|
|
|
def test_get_gitlab_file_returns_empty_dict_for_non_dict_response(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
monkeypatch.setattr(
|
|
"integrations.gitlab.httpx.request",
|
|
lambda *_, **__: _FakeResponse(["not", "a", "dict"]),
|
|
)
|
|
|
|
result = get_gitlab_file(
|
|
config=_make_config(),
|
|
project_id="proj",
|
|
file_path="foo.py",
|
|
)
|
|
|
|
assert result == {}
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# post_gitlab_mr_note
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_post_gitlab_mr_note_uses_post_method(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
captured: dict[str, Any] = {}
|
|
|
|
def _fake_request(method: str, _url: str, **_kw: Any) -> _FakeResponse:
|
|
captured["method"] = method
|
|
return _FakeResponse({"id": 1, "body": "RCA Finding"})
|
|
|
|
monkeypatch.setattr("integrations.gitlab.httpx.request", _fake_request)
|
|
|
|
post_gitlab_mr_note(
|
|
config=_make_config(), project_id="my-org/my-repo", mr_iid="42", body="RCA Finding"
|
|
)
|
|
|
|
assert captured["method"] == "POST"
|
|
|
|
|
|
def test_post_gitlab_mr_note_url_encodes_project_id(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
captured: dict[str, Any] = {}
|
|
|
|
def _fake_request(_method: str, url: str, **_kw: Any) -> _FakeResponse:
|
|
captured["url"] = url
|
|
return _FakeResponse({"id": 1})
|
|
|
|
monkeypatch.setattr("integrations.gitlab.httpx.request", _fake_request)
|
|
|
|
post_gitlab_mr_note(config=_make_config(), project_id="my-org/my-repo", mr_iid="7", body="note")
|
|
|
|
assert "my-org%2Fmy-repo" in captured["url"]
|
|
assert "/merge_requests/7/notes" in captured["url"]
|
|
|
|
|
|
def test_post_gitlab_mr_note_sends_body_in_json(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
captured: dict[str, Any] = {}
|
|
|
|
def _fake_request(_method: str, _url: str, **kw: Any) -> _FakeResponse:
|
|
captured["json"] = kw.get("json")
|
|
return _FakeResponse({"id": 1})
|
|
|
|
monkeypatch.setattr("integrations.gitlab.httpx.request", _fake_request)
|
|
|
|
post_gitlab_mr_note(
|
|
config=_make_config(), project_id="proj", mr_iid="3", body="root cause is X"
|
|
)
|
|
|
|
assert captured["json"] == {"body": "root cause is X"}
|
|
|
|
|
|
def test_post_gitlab_mr_note_returns_empty_dict_for_non_dict_response(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
monkeypatch.setattr(
|
|
"integrations.gitlab.httpx.request",
|
|
lambda *_, **__: _FakeResponse([{"unexpected": "list"}]),
|
|
)
|
|
|
|
result = post_gitlab_mr_note(config=_make_config(), project_id="proj", mr_iid="1", body="note")
|
|
|
|
assert result == {}
|
|
|
|
|
|
def test_post_gitlab_mr_note_returns_created_note_dict(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
note = {"id": 42, "body": "RCA: deployment caused the spike", "author": {"username": "bot"}}
|
|
monkeypatch.setattr(
|
|
"integrations.gitlab.httpx.request",
|
|
lambda *_, **__: _FakeResponse(note),
|
|
)
|
|
|
|
result = post_gitlab_mr_note(
|
|
config=_make_config(),
|
|
project_id="proj",
|
|
mr_iid="5",
|
|
body="RCA: deployment caused the spike",
|
|
)
|
|
|
|
assert result == note
|
|
assert result["id"] == 42
|
|
|
|
|
|
def test_post_gitlab_mr_note_raises_on_403(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setattr(
|
|
"integrations.gitlab.httpx.request",
|
|
lambda *_, **__: _FakeResponse({}, status_code=403),
|
|
)
|
|
|
|
with pytest.raises(httpx.HTTPStatusError):
|
|
post_gitlab_mr_note(config=_make_config(), project_id="proj", mr_iid="1", body="note")
|
|
|
|
|
|
def test_post_gitlab_mr_note_raises_on_404(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setattr(
|
|
"integrations.gitlab.httpx.request",
|
|
lambda *_, **__: _FakeResponse({}, status_code=404),
|
|
)
|
|
|
|
with pytest.raises(httpx.HTTPStatusError):
|
|
post_gitlab_mr_note(config=_make_config(), project_id="proj", mr_iid="999", body="note")
|
|
|
|
|
|
def test_post_gitlab_mr_note_mr_iid_in_url_path(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
captured: dict[str, Any] = {}
|
|
|
|
def _fake_request(_method: str, url: str, **_kw: Any) -> _FakeResponse:
|
|
captured["url"] = url
|
|
return _FakeResponse({"id": 1})
|
|
|
|
monkeypatch.setattr("integrations.gitlab.httpx.request", _fake_request)
|
|
|
|
post_gitlab_mr_note(config=_make_config(), project_id="proj", mr_iid="123", body="note")
|
|
|
|
assert "/merge_requests/123/notes" in captured["url"]
|
|
|
|
|
|
def test_post_gitlab_mr_note_sends_multiline_body(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
captured: dict[str, Any] = {}
|
|
multiline = "## Root Cause\n\nDeployment at 14:32 UTC introduced a memory leak.\n\n**Fix**: rollback to v1.2.3"
|
|
|
|
def _fake_request(_method: str, _url: str, **kw: Any) -> _FakeResponse:
|
|
captured["json"] = kw.get("json")
|
|
return _FakeResponse({"id": 1})
|
|
|
|
monkeypatch.setattr("integrations.gitlab.httpx.request", _fake_request)
|
|
|
|
post_gitlab_mr_note(config=_make_config(), project_id="proj", mr_iid="1", body=multiline)
|
|
|
|
assert captured["json"] == {"body": multiline}
|