fix: annotate POST render endpoints read-only and irreversible POSTs destructive

This commit is contained in:
Latte committed 2026-10-14 16:09:59 +02:00
1 parent 156e963e21
commit 6b11d29919
4 files changed
+107 -22

No files matched your search

+53 -3
View File
@@ -9,7 +9,13 @@ import pytest
from jsonschema import Draft202012Validator
from aegis_gitea_mcp.catalog import Operation, load_catalog
from aegis_gitea_mcp.catalog.build import build_catalog, render_catalog, spec_sha256
from aegis_gitea_mcp.catalog.build import (
DESTRUCTIVE_POST_OPERATIONS,
READ_ONLY_POST_OPERATIONS,
build_catalog,
render_catalog,
spec_sha256,
)
from aegis_gitea_mcp.catalog.schema import (
MAX_DESCRIPTION_CHARS,
SUDO_SCHEMA,
@@ -536,8 +542,12 @@ def test_real_body_and_file_operations() -> None:
def test_real_annotations() -> None:
catalog = load_catalog()
for operation in catalog.operations:
assert operation.read_only == (operation.method == "GET")
assert operation.destructive == (operation.method in {"DELETE", "PUT", "PATCH"})
post_read_only = operation.operation_id in READ_ONLY_POST_OPERATIONS
post_destructive = operation.operation_id in DESTRUCTIVE_POST_OPERATIONS
assert operation.read_only == (operation.method == "GET" or post_read_only)
assert operation.destructive == (
operation.method in {"DELETE", "PUT", "PATCH"} or post_destructive
)
assert not (operation.read_only and operation.destructive)
if operation.paginated:
names = {p.name for p in operation.parameters}
@@ -574,3 +584,43 @@ def test_unknown_parameter_location_is_rejected() -> None:
)
with pytest.raises(ValueError, match="unknown parameter location"):
build_catalog(json.dumps(spec).encode())
def test_post_annotation_overrides_in_the_real_catalog() -> None:
by_id = {op.operation_id: op for op in load_catalog().operations}
for operation_id in ("renderMarkdown", "renderMarkdownRaw", "renderMarkup"):
operation = by_id[operation_id]
assert operation.method == "POST"
assert operation.read_only and not operation.destructive and operation.idempotent
for operation_id in (
"repoMergePullRequest",
"repoTransfer",
"acceptRepoTransfer",
"rejectRepoTransfer",
"adminRenameUser",
"renameOrg",
"adminCronRun",
"repoMirrorSync",
"adminAdoptRepository",
"adminDeleteUnadoptedRepository",
"repoRenameBranch",
):
operation = by_id[operation_id]
assert operation.destructive and not operation.read_only, operation_id
# Ordinary creating POSTs stay non-destructive.
for operation_id in ("issueCreateIssue", "repoCreatePullRequest", "repoCreateBranch"):
assert not by_id[operation_id].destructive
assert not by_id[operation_id].read_only
def test_every_annotation_override_names_a_real_post_operation() -> None:
posts = {op.operation_id for op in load_catalog().operations if op.method == "POST"}
assert READ_ONLY_POST_OPERATIONS <= posts
assert DESTRUCTIVE_POST_OPERATIONS <= posts
assert not READ_ONLY_POST_OPERATIONS & DESTRUCTIVE_POST_OPERATIONS
def test_override_applies_to_a_mini_spec() -> None:
spec = mini_spec({"/markdown": {"post": {"operationId": "renderMarkdown"}}})
operation = build_ops(spec)["renderMarkdown"]
assert operation.read_only and not operation.destructive and operation.idempotent