#!/usr/bin/env python3 # -*- coding: utf-8 -*- """Test integrazione Gitea MCP — mock server + opzionale live API.""" from __future__ import annotations import json import os import sys import threading from http.server import BaseHTTPRequestHandler, HTTPServer from urllib.parse import parse_qs, urlparse ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) if ROOT not in sys.path: sys.path.insert(0, ROOT) MOCK_TOKEN = "test-gitea-token" MOCK_PORT = 18799 os.environ["GITEA_URL"] = "https://git.loogle.it" os.environ["GITEA_API_URL"] = f"http://127.0.0.1:{MOCK_PORT}" os.environ["GITEA_API_TOKEN_DANIELE"] = MOCK_TOKEN os.environ["MCP_DB"] = "/tmp/loogle_mcp_test.db" class GiteaMockHandler(BaseHTTPRequestHandler): def log_message(self, fmt, *args): return def _auth_ok(self) -> bool: auth = self.headers.get("Authorization", "") return auth == f"token {MOCK_TOKEN}" def _json(self, code: int, payload): body = json.dumps(payload).encode() self.send_response(code) self.send_header("Content-Type", "application/json") self.send_header("Content-Length", str(len(body))) self.end_headers() self.wfile.write(body) def do_GET(self): if not self._auth_ok(): self._json(401, {"message": "invalid token"}) return path = urlparse(self.path).path qs = parse_qs(urlparse(self.path).query) if path == "/api/v1/user/repos": self._json(200, [ { "full_name": "daniele/rete", "description": "Infra HA", "private": True, "html_url": "https://git.loogle.it/daniele/rete", "default_branch": "main", "updated_at": "2026-08-22T10:00:00Z", } ]) return if path == "/api/v1/repos/daniele/rete/contents/ha/RUNBOOK-failover.md": content = "# Failover tier-b\n\nloogle-mcp nel pivot tier-b.\n" import base64 self._json( 200, { "type": "file", "path": "ha/RUNBOOK-failover.md", "content": base64.b64encode(content.encode()).decode(), "encoding": "base64", "size": len(content), "sha": "abc123", "html_url": "https://git.loogle.it/daniele/rete/src/branch/main/ha/RUNBOOK-failover.md", }, ) return if path == "/api/v1/search/code": q = (qs.get("q") or [""])[0] self._json( 200, { "data": [ { "repository": {"full_name": "daniele/rete"}, "path": "ha/RUNBOOK-failover.md", "sha": "abc123", "content": f"...{q}...", } ] }, ) return if path == "/api/v1/repos/daniele/rete/issues": self._json( 200, [ { "number": 1, "title": "Test issue", "state": "open", "user": {"login": "daniele"}, "html_url": "https://git.loogle.it/daniele/rete/issues/1", "created_at": "2026-08-22T10:00:00Z", "updated_at": "2026-08-22T10:00:00Z", "labels": [], } ], ) return if path == "/api/v1/repos/daniele/rete/issues/1": self._json( 200, { "number": 1, "title": "Test issue", "state": "open", "body": "Corpo issue di test", "user": {"login": "daniele"}, "html_url": "https://git.loogle.it/daniele/rete/issues/1", "created_at": "2026-08-22T10:00:00Z", "updated_at": "2026-08-22T10:00:00Z", "labels": [], }, ) return self._json(404, {"message": f"not found: {path}"}) def do_POST(self): if not self._auth_ok(): self._json(401, {"message": "invalid token"}) return path = urlparse(self.path).path length = int(self.headers.get("Content-Length", 0)) body = json.loads(self.rfile.read(length).decode() or "{}") if path == "/api/v1/repos/daniele/rete/issues": self._json( 201, { "number": 42, "title": body.get("title"), "state": "open", "html_url": "https://git.loogle.it/daniele/rete/issues/42", }, ) return self._json(404, {"message": f"not found: {path}"}) def run_mock_tests() -> None: from app.db import init_db from app.knowledge import gitea as gitea_mod from app.mcp import tools init_db() gitea_mod._tokens_cache = None server = HTTPServer(("127.0.0.1", MOCK_PORT), GiteaMockHandler) thread = threading.Thread(target=server.serve_forever, daemon=True) thread.start() claims = {"sub": "daniele", "scope": "gitea:read gitea:write"} repos = tools.call_tool("list_repos", {}, claims) assert "daniele/rete" in repos["content"][0]["text"] file_data = tools.call_tool( "get_file", {"repo": "daniele/rete", "path": "ha/RUNBOOK-failover.md"}, claims, ) assert "tier-b" in file_data["content"][0]["text"] search = tools.call_tool( "search_code", {"query": "tier-b", "repo": "daniele/rete"}, claims, ) assert "RUNBOOK-failover" in search["content"][0]["text"] issues = tools.call_tool("list_issues", {"repo": "daniele/rete"}, claims) assert "Test issue" in issues["content"][0]["text"] issue = tools.call_tool( "get_issue", {"repo": "daniele/rete", "number": 1}, claims, ) assert "Corpo issue" in issue["content"][0]["text"] created = tools.call_tool( "create_issue", {"repo": "daniele/rete", "title": "Da MCP", "body": "Test"}, claims, ) assert "42" in created["content"][0]["text"] server.shutdown() print("OK mock: list_repos, get_file, search_code, list_issues, get_issue, create_issue") def _load_dotenv() -> None: env_path = os.path.join(ROOT, ".env") if not os.path.isfile(env_path): return with open(env_path, encoding="utf-8") as fh: for line in fh: line = line.strip() if not line or line.startswith("#") or "=" not in line: continue key, _, value = line.partition("=") key = key.strip() value = value.strip().strip("'").strip('"') if key.startswith("GITEA_"): os.environ[key] = value def run_live_tests() -> None: _load_dotenv() from app.knowledge import gitea as gitea_mod from app.mcp import tools token = os.environ.get("GITEA_API_TOKEN_DANIELE", "").strip() if not token or token == MOCK_TOKEN: print("SKIP live: GITEA_API_TOKEN_DANIELE non impostato (token reale in .env)") return api_url = gitea_mod.api_base_url() if str(MOCK_PORT) in api_url: print("SKIP live: GITEA_API_URL punta al mock") return gitea_mod._tokens_cache = None claims = {"sub": "daniele", "scope": "gitea:read gitea:write"} repos = gitea_mod.list_repos(username="daniele") if not repos.get("repos"): raise RuntimeError("live list_repos: nessun repository") first = repos["repos"][0]["full_name"] tools.call_tool("list_repos", {}, claims) print(f"OK live list_repos: {len(repos['repos'])} repo, primo={first}") def main() -> int: run_mock_tests() run_live_tests() return 0 if __name__ == "__main__": raise SystemExit(main())