Files
rune/tests/test_gitea_auth.py
T
Nick 5b268fde8e
CI Pipeline / best-practices (push) Successful in 8s
Auto-version and Release / release (push) Successful in 15s
CI Pipeline / documentation (push) Successful in 6s
CI Pipeline / canary (push) Successful in 34s
CI Pipeline / lint (push) Failing after 39s
CI Pipeline / type-check (push) Successful in 27s
CI Pipeline / gitea-offline (push) Successful in 15s
CI Pipeline / unit-tests (push) Successful in 1m2s
CI Pipeline / required-checks (push) Successful in 1s
deploy / deploy (push) Successful in 38s
fix(ci+release): format new auth test; repair f-string backslash in release idempotency check
- tests/test_gitea_auth.py: ruff format (CI format-gate on added files)
- auto-version.yml release step: f-string with escaped quotes is a
  SyntaxError on py<3.12 runners; compute tag name without backslash
  escapes (release job exit 22, run 6776)
2026-09-11 20:59:39 +01:00

110 lines
3.6 KiB
Python

"""Canonical Gitea auth path (handoff 2026-09-11 Part B).
Proves: env credential path works; credential-helper fallback works;
unavailable credentials fail clearly; no token ever appears in exception
or log output.
"""
import importlib.util
import subprocess
import sys
import unittest
from pathlib import Path
from unittest import mock
ROOT = Path(__file__).resolve().parent.parent
_spec = importlib.util.spec_from_file_location("gitea_auth", ROOT / "scripts" / "gitea_auth.py")
ga = importlib.util.module_from_spec(_spec)
_spec.loader.exec_module(ga)
SECRET = "unit-test-secret-token-value"
class TestGiteaAuth(unittest.TestCase):
def setUp(self):
import os
os_env = {k: v for k, v in os.environ.items() if k not in ("GITEA_TOKEN", "GITEA_TOKEN_FILE")}
self.patcher = mock.patch.dict(os.environ, os_env, clear=True)
self.patcher.start()
self.addCleanup(self.patcher.stop)
# Isolate from the machine's real token file and real credential store.
self._nogit = mock.patch.object(ga, "DEFAULT_TOKEN_FILE", Path("/nonexistent/gitea-token-isolation"))
self._nogit.start()
self.addCleanup(self._nogit.stop)
def test_env_token_used(self):
import os
os.environ["GITEA_TOKEN"] = SECRET
self.assertEqual(ga.get_token(), SECRET)
def test_token_file_fallback(self):
"""Env absent, GITEA_TOKEN_FILE set → file token used."""
import os
import tempfile
fd, name = tempfile.mkstemp()
os.close(fd)
os.chmod(name, 0o600)
Path(name).write_text(SECRET)
try:
os.environ["GITEA_TOKEN_FILE"] = name
self.assertEqual(ga.get_token(), SECRET)
finally:
os.unlink(name)
def test_token_file_bad_mode_rejected(self):
import os
import tempfile
fd, name = tempfile.mkstemp()
os.close(fd)
os.chmod(name, 0o644)
Path(name).write_text(SECRET)
try:
os.environ["GITEA_TOKEN_FILE"] = name
with self.assertRaises(PermissionError) as ctx:
ga.get_token()
self.assertNotIn(SECRET, str(ctx.exception))
finally:
os.unlink(name)
def test_credential_helper_fallback(self):
"""No env, no file → git credential fill is consulted."""
import os
fake = subprocess.CompletedProcess([], 0, stdout=f"password={SECRET}\n", stderr="")
with mock.patch.object(ga.subprocess, "run", return_value=fake) as m:
token = ga.get_token()
self.assertEqual(token, SECRET)
self.assertIn("credential", m.call_args[0][0])
def test_unavailable_fails_clearly_without_token(self):
err = None
with mock.patch.object(
ga.subprocess, "run", return_value=subprocess.CompletedProcess([], 0, stdout="", stderr="")
):
try:
ga.get_token()
except ga.GiteaCredentialError as e:
err = e
self.assertIsNotNone(err, "must raise GiteaCredentialError when no credential source answers")
self.assertNotIn(SECRET, str(err))
self.assertIn("GITEA_TOKEN", str(err)) # actionable, names the remedy
def test_no_token_in_any_error_path(self):
"""The helper must never leak a token through an exception message."""
import os
os.environ["GITEA_TOKEN"] = SECRET
try:
with mock.patch.object(ga.Path, "is_file", side_effect=RuntimeError(SECRET)):
ga.get_token()
except Exception as e:
self.fail(f"unexpected exception leaked through: {e}")
if __name__ == "__main__":
unittest.main()