blob: 65dffb3f52be55c3698727a4b9d938c1dba7e021 [file]
# 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.
"""
Tests for the gerrit_auth module.
"""
import os
import unittest
from unittest.mock import mock_open, patch
from gerrit_mcp_server import gerrit_auth
class TestGerritAuth(unittest.TestCase):
def test_get_auth_for_gob(self):
"""Tests that the correct command is returned for gob-curl."""
self.assertEqual(gerrit_auth._get_auth_for_gob({}), ["gob-curl", "-s"])
def test_get_auth_for_http_basic_success(self):
"""Tests that the correct command is returned for http_basic auth."""
config = {"username": "testuser", "auth_token": "secret"}
expected = ["curl", "--user", "testuser:secret", "-L"]
self.assertEqual(gerrit_auth._get_auth_for_http_basic(config), expected)
def test_get_auth_for_http_basic_netrc(self):
"""Tests that --netrc is used when no credentials are configured."""
self.assertEqual(
gerrit_auth._get_auth_for_http_basic({}),
["curl", "--netrc", "-L"],
)
@patch("os.path.exists", return_value=True)
def test_get_auth_for_http_basic_netrc_file(self, mock_exists):
"""Tests that --netrc-file is used when netrc_path is configured."""
config = {"netrc_path": "~/custom_netrc"}
command = gerrit_auth._get_auth_for_http_basic(config)
self.assertEqual(
command,
["curl", "--netrc-file", os.path.expanduser("~/custom_netrc"), "-L"],
)
def test_get_auth_for_http_basic_missing_username(self):
"""Tests that an error is raised if only auth_token is provided."""
with self.assertRaisesRegex(ValueError, "provide both 'username' and"):
gerrit_auth._get_auth_for_http_basic({"auth_token": "secret"})
def test_get_auth_for_http_basic_missing_token(self):
"""Tests that an error is raised if only username is provided."""
with self.assertRaisesRegex(ValueError, "provide both 'username' and"):
gerrit_auth._get_auth_for_http_basic({"username": "testuser"})
@patch("os.path.exists", return_value=True)
def test_get_auth_for_gitcookies_success(self, mock_exists):
"""Tests that the correct command is returned for gitcookies auth."""
config = {"gitcookies_path": "~/.gitcookies"}
url = "https://my-gerrit.com"
m = mock_open(
read_data="my-gerrit.com\tFALSE\t/\tTRUE\t2147483647\to\tgit-token"
)
with patch("builtins.open", m):
command = gerrit_auth._get_auth_for_gitcookies(url, config)
self.assertEqual(command, ["curl", "-b", "o=git-token", "-L"])
@patch("os.path.exists", return_value=False)
def test_get_auth_for_gitcookies_file_not_found(self, mock_exists):
"""Tests fallback to unauthenticated curl if gitcookies file is not found."""
config = {"gitcookies_path": "~/.gitcookies"}
url = "https://my-gerrit.com"
command = gerrit_auth._get_auth_for_gitcookies(url, config)
self.assertEqual(command, ["curl", "-s", "-L"])
def test_get_auth_for_gitcookies_missing_path(self):
"""Tests that an error is raised if gitcookies_path is missing."""
with self.assertRaisesRegex(ValueError, "requires 'gitcookies_path'"):
gerrit_auth._get_auth_for_gitcookies("https://a.com", {})
@patch("os.path.exists", return_value=True)
def test_get_auth_for_gitcookies_selects_last_entry(self, mock_exists):
"""Tests that _get_auth_for_gitcookies selects the last matching
cookie entry."""
config = {"gitcookies_path": "~/.gitcookies"}
url = "https://my-gerrit.com"
multi_cookie_content = (
"other-gerrit.com\tFALSE\t/\tTRUE\t2147483647\to\tgit-oldtoken\n"
"my-gerrit.com\tFALSE\t/\tTRUE\t2147483647\to\tgit-firsttoken\n"
"another-gerrit.com\tFALSE\t/\tTRUE\t2147483647\to\tgit-anothertoken\n"
"my-gerrit.com\tFALSE\t/\tTRUE\t2147483647\to\tgit-lasttoken"
)
m = mock_open(read_data=multi_cookie_content)
with patch("builtins.open", m):
command = gerrit_auth._get_auth_for_gitcookies(url, config)
self.assertEqual(command, ["curl", "-b", "o=git-lasttoken", "-L"])
@patch("os.path.exists", return_value=True)
def test_get_auth_for_gitcookies_wildcard_matches_subdomain(self, mock_exists):
"""A leading-dot (wildcard) entry matches a subdomain of that domain."""
config = {"gitcookies_path": "~/.gitcookies"}
url = "https://sub.my-gerrit.com"
m = mock_open(
read_data=".my-gerrit.com\tTRUE\t/\tTRUE\t2147483647\to\tgit-token"
)
with patch("builtins.open", m):
command = gerrit_auth._get_auth_for_gitcookies(url, config)
self.assertEqual(command, ["curl", "-b", "o=git-token", "-L"])
@patch("os.path.exists", return_value=True)
def test_get_auth_for_gitcookies_wildcard_matches_exact(self, mock_exists):
"""A leading-dot (wildcard) entry also matches the bare domain."""
config = {"gitcookies_path": "~/.gitcookies"}
url = "https://my-gerrit.com"
m = mock_open(
read_data=".my-gerrit.com\tTRUE\t/\tTRUE\t2147483647\to\tgit-token"
)
with patch("builtins.open", m):
command = gerrit_auth._get_auth_for_gitcookies(url, config)
self.assertEqual(command, ["curl", "-b", "o=git-token", "-L"])
@patch("os.path.exists", return_value=True)
def test_get_auth_for_gitcookies_non_wildcard_ignores_subdomain(self, mock_exists):
"""A non-wildcard entry must NOT match a subdomain of that domain."""
config = {"gitcookies_path": "~/.gitcookies"}
url = "https://sub.my-gerrit.com"
m = mock_open(
read_data="my-gerrit.com\tFALSE\t/\tTRUE\t2147483647\to\tgit-token"
)
with patch("builtins.open", m):
command = gerrit_auth._get_auth_for_gitcookies(url, config)
# No matching entry, so it falls back to an unauthenticated request.
self.assertEqual(command, ["curl", "-s", "-L"])
if __name__ == "__main__":
unittest.main()