204 lines
5.8 KiB
Python
204 lines
5.8 KiB
Python
"""Draft-creation input contracts for the Atlas Gitea client."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from testing.tests.test_hermes_gitea_support import (
|
|
HEAD_SHA,
|
|
Response,
|
|
_draft_payload,
|
|
_draft_response,
|
|
_load,
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("method", "path", "data"),
|
|
[
|
|
("DELETE", "/api/v1/repos/titan/cassandra/pulls/4", None),
|
|
("POST", "/api/v1/repos/titan/cassandra/pulls/4/merge", {}),
|
|
(
|
|
"POST",
|
|
"/api/v1/repos/titan/cassandra/pulls/4/reviews",
|
|
{"event": "APPROVED"},
|
|
),
|
|
("PATCH", "/api/v1/repos/titan/cassandra/pulls/4", {"title": "WIP: x"}),
|
|
("PUT", "/api/v1/repos/titan/cassandra/branches/main", {}),
|
|
],
|
|
)
|
|
def test_merge_approve_close_delete_update_and_other_mutations_are_rejected(
|
|
method: str, path: str, data: object
|
|
):
|
|
client = _load()
|
|
|
|
with pytest.raises(client.PolicyError):
|
|
client.build_request(
|
|
method, path, base_url=client.CANONICAL_BASE_URL, token="secret", data=data
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"ref",
|
|
[
|
|
"foo/.bar",
|
|
"foo/bar.lock/baz",
|
|
"foo..bar",
|
|
"foo@{bar",
|
|
"foo//bar",
|
|
"-danger",
|
|
"danger.",
|
|
"danger~one",
|
|
"danger^one",
|
|
"danger:one",
|
|
"danger one",
|
|
],
|
|
)
|
|
def test_complete_git_ref_validation_rejects_invalid_names(ref: str):
|
|
client = _load()
|
|
|
|
with pytest.raises(client.PolicyError):
|
|
client._validate_ref(ref, "head")
|
|
|
|
|
|
def test_git_ref_validation_uses_fixed_trusted_binary():
|
|
client = _load()
|
|
|
|
assert client._validate_ref.__globals__["GIT_BIN"] == "/usr/bin/git"
|
|
assert client._validate_ref("hermes/valid-fix", "head") == "hermes/valid-fix"
|
|
|
|
|
|
@pytest.mark.parametrize("field", ["base", "head"])
|
|
@pytest.mark.parametrize("oversized", ["r" * 100_000, "🧪" * 128])
|
|
def test_oversized_ref_never_invokes_git_request_or_opener(
|
|
field: str, oversized: str, monkeypatch
|
|
):
|
|
client = _load()
|
|
git_called = False
|
|
request_built = False
|
|
opener_called = False
|
|
original_build_request = client.build_request
|
|
|
|
def git_run(*_args, **_kwargs):
|
|
nonlocal git_called
|
|
git_called = True
|
|
raise AssertionError("Git must not receive an oversized ref")
|
|
|
|
def build_request(*args, **kwargs):
|
|
nonlocal request_built
|
|
request_built = True
|
|
return original_build_request(*args, **kwargs)
|
|
|
|
def opener(*_args, **_kwargs):
|
|
nonlocal opener_called
|
|
opener_called = True
|
|
return Response(_draft_response())
|
|
|
|
monkeypatch.setattr(client._validate_ref.__globals__["subprocess"], "run", git_run)
|
|
client.build_request = build_request
|
|
refs = {"base": "main", "head": "hermes/fix"}
|
|
refs[field] = oversized
|
|
with pytest.raises(client.PolicyError, match="branch-name limit"):
|
|
client.create_draft(
|
|
"cassandra",
|
|
base=refs["base"],
|
|
head=refs["head"],
|
|
head_sha=HEAD_SHA,
|
|
title="Focused fix",
|
|
body="Review evidence",
|
|
token="runtime",
|
|
opener=opener,
|
|
)
|
|
assert git_called is False
|
|
assert request_built is False
|
|
assert opener_called is False
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("field", "oversized"),
|
|
[("repo", "r" * 101), ("title", "🧪" * 200), ("body", "🧪" * 9_000)],
|
|
)
|
|
def test_other_text_bounds_fail_before_git_request_or_opener(
|
|
field: str, oversized: str, monkeypatch
|
|
):
|
|
client = _load()
|
|
git_called = False
|
|
request_built = False
|
|
opener_called = False
|
|
original_build_request = client.build_request
|
|
|
|
def git_run(*_args, **_kwargs):
|
|
nonlocal git_called
|
|
git_called = True
|
|
raise AssertionError("Git must not run before cheap input bounds")
|
|
|
|
def build_request(*args, **kwargs):
|
|
nonlocal request_built
|
|
request_built = True
|
|
return original_build_request(*args, **kwargs)
|
|
|
|
def opener(*_args, **_kwargs):
|
|
nonlocal opener_called
|
|
opener_called = True
|
|
return Response(_draft_response())
|
|
|
|
monkeypatch.setattr(client._validate_ref.__globals__["subprocess"], "run", git_run)
|
|
client.build_request = build_request
|
|
values = {
|
|
"repo": "cassandra",
|
|
"title": "Focused fix",
|
|
"body": "Review evidence",
|
|
}
|
|
values[field] = oversized
|
|
with pytest.raises(client.PolicyError):
|
|
client.create_draft(
|
|
values["repo"],
|
|
base="main",
|
|
head="hermes/fix",
|
|
head_sha=HEAD_SHA,
|
|
title=values["title"],
|
|
body=values["body"],
|
|
token="runtime",
|
|
opener=opener,
|
|
)
|
|
assert git_called is False
|
|
assert request_built is False
|
|
assert opener_called is False
|
|
|
|
|
|
def test_create_forces_draft_title_and_same_repository_branch_names():
|
|
client = _load()
|
|
assert (
|
|
client.authorize_request(
|
|
"POST", "/api/v1/repos/titan/cassandra/pulls", _draft_payload()
|
|
)
|
|
== "create-draft"
|
|
)
|
|
with pytest.raises(client.PolicyError, match="draft-title prefix"):
|
|
client.authorize_request(
|
|
"POST",
|
|
"/api/v1/repos/titan/cassandra/pulls",
|
|
_draft_payload(title="Not a draft"),
|
|
)
|
|
with pytest.raises(client.PolicyError):
|
|
client.authorize_request(
|
|
"POST",
|
|
"/api/v1/repos/titan/cassandra/pulls",
|
|
_draft_payload(head="someone:branch"),
|
|
)
|
|
|
|
|
|
def test_runtime_token_is_only_an_authorization_header():
|
|
client = _load()
|
|
request = client.build_request(
|
|
"POST",
|
|
"/api/v1/repos/titan/cassandra/pulls",
|
|
base_url=client.CANONICAL_BASE_URL,
|
|
token="do-not-leak",
|
|
data=_draft_payload(),
|
|
)
|
|
|
|
assert "do-not-leak" not in request.full_url
|
|
assert b"do-not-leak" not in request.data
|
|
assert request.get_header("Authorization") == "token do-not-leak"
|