blob: 0d6c1d2d31bfa5ef905abd166145ff1ccffd960e [file] [log] [blame]
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import json
import os
import re
import uuid
import pytest
from gerrit_mcp_server import main
# --- Fixtures ---
@pytest.fixture(scope="module")
def e2e_config():
"""
Loads the E2E test configuration from a JSON file.
Skips all tests in the module if the file is missing.
"""
config_path = os.path.join(os.path.dirname(__file__), "e2e_config.json")
if not os.path.exists(config_path):
pytest.skip(
"E2E config file 'tests/e2e/e2e_config.json' not found. "
"Copy 'tests/e2e/e2e_config.sample.json' to create one.",
allow_module_level=True
)
with open(config_path, "r") as f:
config = json.load(f)
return config
@pytest.fixture
def gerrit_base_url(e2e_config):
return e2e_config.get("gerrit_base_url")
@pytest.fixture
def known_cl(e2e_config):
return e2e_config.get("known_cl")
@pytest.fixture
def known_user(e2e_config):
return e2e_config.get("known_user")
@pytest.fixture
def test_project(e2e_config):
project = e2e_config.get("test_project")
if not project:
pytest.skip("Skipping write tests: 'test_project' not configured.")
return project
@pytest.fixture
def test_reviewer(e2e_config):
return e2e_config.get("test_reviewer")
@pytest.fixture
async def created_change(gerrit_base_url):
"""
Yields a function that creates a change, and ensures it is abandoned during teardown.
This acts as a context manager for the test data.
"""
created_cl_ids = []
async def _create(project, subject, branch="main"):
result = await main.create_change(
project=project,
subject=subject,
branch=branch,
gerrit_base_url=gerrit_base_url,
)
# Extract the CL number to use for abandonment
match = re.search(r"new change (\d+)", result[0]["text"])
if match:
created_cl_ids.append(match.group(1))
return result, match.group(1) if match else None
yield _create
# Teardown: Abandon all created changes
for cl_id in created_cl_ids:
print(f"Cleaning up by abandoning CL {cl_id}...")
try:
await main.abandon_change(
change_id=cl_id,
gerrit_base_url=gerrit_base_url,
message="Cleaning up after E2E test.",
)
print(f"CL {cl_id} abandoned.")
except Exception as e:
print(f"Failed to abandon CL {cl_id}: {e}")
# --- Read-Only Tests ---
@pytest.mark.asyncio
async def test_e2e_query_changes(gerrit_base_url):
"""Tests that we can query open changes from the Gerrit instance."""
result = await main.query_changes(
query="status:open", gerrit_base_url=gerrit_base_url, limit=5
)
assert "Found 5 changes" in result[0]["text"]
@pytest.mark.asyncio
async def test_e2e_get_change_details(gerrit_base_url, known_cl):
"""Tests that we can get the details of a known CL."""
result = await main.get_change_details(
change_id=known_cl, gerrit_base_url=gerrit_base_url
)
text = result[0]["text"]
assert f"Summary for CL {known_cl}" in text
assert "Subject:" in text
@pytest.mark.asyncio
async def test_e2e_list_change_files(gerrit_base_url, known_cl):
"""Tests that we can list the files of a known CL."""
result = await main.list_change_files(
change_id=known_cl, gerrit_base_url=gerrit_base_url
)
assert f"Files in CL {known_cl}" in result[0]["text"]
@pytest.mark.asyncio
async def test_e2e_get_most_recent_cl(gerrit_base_url, known_user):
"""Tests that we can get the most recent CL for a known user."""
result = await main.get_most_recent_cl(
user=known_user, gerrit_base_url=gerrit_base_url
)
text = result[0]["text"]
# The test should pass if a CL is found OR if it correctly reports no changes.
assert (
f"Most recent CL for {known_user}" in text
or f"No changes found for user: {known_user}" in text
)
# --- Write Tests ---
@pytest.mark.asyncio
async def test_e2e_create_and_abandon_change(test_project, gerrit_base_url, created_change):
"""Tests the full lifecycle of creating and abandoning a change."""
subject = f"E2E Test: Create and Abandon - {uuid.uuid4()}"
# created_change fixture handles the creation and automatic cleanup (abandonment)
result, cl_id = await created_change(project=test_project, subject=subject)
assert "Successfully created new change" in result[0]["text"]
assert cl_id is not None
# Verify the creation was successful by fetching details
details = await main.get_change_details(
cl_id, gerrit_base_url=gerrit_base_url
)
assert subject in details[0]["text"]