#!/usr/bin/env python3
"""
Quick test suite for Gemini MCP Server with timeouts.
"""
import sys
import os
import signal
import functools
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from server import (
run_gemini,
gemini_resume,
gemini_list_sessions,
gemini_list_extensions,
gemini_version,
strip_ansi,
filter_stderr,
process_gemini_output
)
# Timeout decorator
class TimeoutError(Exception):
pass
def timeout(seconds):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
def handler(signum, frame):
raise TimeoutError(f"Test timed out after {seconds}s")
old_handler = signal.signal(signal.SIGALRM, handler)
signal.alarm(seconds)
try:
result = func(*args, **kwargs)
finally:
signal.alarm(0)
signal.signal(signal.SIGALRM, old_handler)
return result
return wrapper
return decorator
passed = 0
failed = 0
def test(name: str, func, check, timeout_sec=30):
"""Run a single test with timeout."""
global passed, failed
@timeout(timeout_sec)
def run_test():
return func()
try:
result = run_test()
success = check(result)
if success:
passed += 1
print(f"✅ {name}")
else:
failed += 1
print(f"❌ {name}")
print(f" Result: {str(result)[:100]}")
except TimeoutError as e:
failed += 1
print(f"⏱️ {name} - TIMEOUT")
except Exception as e:
failed += 1
print(f"❌ {name} - ERROR: {e}")
def main():
print("=" * 50)
print("GEMINI MCP - QUICK TEST SUITE (30 Tests)")
print("=" * 50)
# HELPER FUNCTIONS (no Gemini calls - fast)
print("\n--- Helper Functions (6 tests) ---")
test("T01: strip_ansi with codes",
lambda: strip_ansi("\x1b[32mGreen\x1b[0m"),
lambda r: r == "Green")
test("T02: strip_ansi plain text",
lambda: strip_ansi("Plain text"),
lambda r: r == "Plain text")
test("T03: filter_stderr removes noise",
lambda: filter_stderr("[STARTUP] test\nReal error"),
lambda r: "Real error" in r and "[STARTUP]" not in r)
test("T04: filter_stderr empty string",
lambda: filter_stderr(""),
lambda r: r == "")
test("T05: filter_stderr keep_extensions",
lambda: filter_stderr("Installed extensions:\n- test", keep_extensions=True),
lambda r: "test" in r)
test("T06: filter_stderr loading extension",
lambda: filter_stderr("Loading extension: test\nActual error"),
lambda r: "Actual error" in r and "Loading extension" not in r)
# BASIC TOOLS (actual Gemini calls)
print("\n--- Basic Tools (4 tests) ---")
test("T07: gemini_version",
gemini_version,
lambda r: r and "." in r and "Error" not in r,
timeout_sec=15)
test("T08: gemini_list_extensions",
gemini_list_extensions,
lambda r: r and ("extension" in r.lower() or "-" in r),
timeout_sec=15)
test("T09: gemini_list_sessions",
gemini_list_sessions,
lambda r: r is not None,
timeout_sec=15)
test("T10: gemini_resume latest",
lambda: gemini_resume("latest"),
lambda r: r is not None,
timeout_sec=20)
# MATH PROMPTS
print("\n--- Math Prompts (5 tests) ---")
test("T11: Simple addition (5+5)",
lambda: run_gemini("5+5=? Just the number"),
lambda r: "10" in r,
timeout_sec=30)
test("T12: Multiplication (7*8)",
lambda: run_gemini("Calculate 7 multiplied by 8. Only reply with the single numeric answer."),
lambda r: "56" in r,
timeout_sec=30)
test("T13: Division (100/4)",
lambda: run_gemini("100/4=? Number only"),
lambda r: "25" in r,
timeout_sec=30)
test("T14: Subtraction (50-17)",
lambda: run_gemini("50-17=? Just number"),
lambda r: "33" in r,
timeout_sec=30)
test("T15: Power (2^8)",
lambda: run_gemini("2 to power 8 = ? Just number"),
lambda r: "256" in r,
timeout_sec=30)
# CODE GENERATION
print("\n--- Code Generation (3 tests) ---")
test("T16: Python function",
lambda: run_gemini("Show me a Python function to add two numbers. Print the code only, no files."),
lambda r: "def " in r or "return" in r or "+" in r,
timeout_sec=30)
test("T17: JavaScript",
lambda: run_gemini("const add = (a,b) => a+b; confirm this JS works"),
lambda r: r and len(r) > 0,
timeout_sec=30)
test("T18: SQL query",
lambda: run_gemini("SELECT * FROM users WHERE age > 18; what does this do?"),
lambda r: "select" in r.lower() or "user" in r.lower(),
timeout_sec=30)
# KNOWLEDGE
print("\n--- Knowledge Queries (4 tests) ---")
test("T19: Capital of France",
lambda: run_gemini("Capital of France? One word"),
lambda r: "Paris" in r,
timeout_sec=30)
test("T20: Water formula",
lambda: run_gemini("Chemical formula for water?"),
lambda r: "H2O" in r or "H₂O" in r,
timeout_sec=30)
test("T21: Python creator",
lambda: run_gemini("Who created Python? Just the name"),
lambda r: "Guido" in r or "Rossum" in r,
timeout_sec=30)
test("T22: Yes/No question",
lambda: run_gemini("Is water wet? Yes or no only"),
lambda r: "yes" in r.lower() or "no" in r.lower(),
timeout_sec=30)
# EDGE CASES
print("\n--- Edge Cases (4 tests) ---")
test("T23: Short prompt",
lambda: run_gemini("Hi"),
lambda r: r and len(r) > 0 and "Error" not in r,
timeout_sec=30)
test("T24: Special chars",
lambda: run_gemini("What is two plus two? Answer with just the number."),
lambda r: "4" in r or "four" in r.lower(),
timeout_sec=30)
test("T25: Quotes in prompt",
lambda: run_gemini('What is "hello" in Spanish?'),
lambda r: r and len(r) > 0,
timeout_sec=30)
test("T26: Unicode/emoji",
lambda: run_gemini("❤️ What emoji is this?"),
lambda r: r and len(r) > 0,
timeout_sec=30)
# PARAMETER VARIATIONS
print("\n--- Parameter Variations (4 tests) ---")
test("T27: With sandbox=True",
lambda: run_gemini("1+1=?", sandbox=True),
lambda r: r and "gemini" not in r.lower() or "2" in r,
timeout_sec=30)
test("T28: With yolo=False",
lambda: run_gemini("2+2=?", yolo=False),
lambda r: r is not None,
timeout_sec=30)
test("T29: With model param",
lambda: run_gemini("3+3=?", model="gemini-2.0-flash-exp"),
lambda r: r is not None,
timeout_sec=30)
test("T30: All params combined",
lambda: run_gemini("4+4=?", model="gemini-2.0-flash-exp", sandbox=False, yolo=True),
lambda r: "8" in r or r is not None,
timeout_sec=30)
# STRESS TESTS
print("\n--- Stress Tests (10 tests) ---")
test("T31: Long prompt",
lambda: run_gemini("This is a test with a longer prompt. Please tell me: What is 9 times 9? Only the numeric answer."),
lambda r: "81" in r,
timeout_sec=30)
test("T32: Technical question",
lambda: run_gemini("What is HTTP status code 404? Brief answer."),
lambda r: "not found" in r.lower() or "404" in r,
timeout_sec=30)
test("T33: Multiple numbers",
lambda: run_gemini("What is 10 + 20 + 30? Just the number."),
lambda r: "60" in r,
timeout_sec=30)
test("T34: Negative numbers",
lambda: run_gemini("What is 10 minus 15? Just the number."),
lambda r: "-5" in r or "5" in r,
timeout_sec=30)
test("T35: Decimal math",
lambda: run_gemini("What is 7.5 + 2.5? Just the number."),
lambda r: "10" in r,
timeout_sec=30)
test("T36: Question mark handling",
lambda: run_gemini("What is a variable in programming???"),
lambda r: r and len(r) > 10,
timeout_sec=30)
test("T37: Exclamation handling",
lambda: run_gemini("Hello! What is 6 times 6?!"),
lambda r: "36" in r or r and len(r) > 0,
timeout_sec=30)
test("T38: Boolean response",
lambda: run_gemini("Is Python a programming language? Answer true or false."),
lambda r: "true" in r.lower() or "yes" in r.lower(),
timeout_sec=30)
test("T39: Empty-like prompt",
lambda: run_gemini(" "),
lambda r: r is not None, # Should handle gracefully
timeout_sec=30)
test("T40: Backticks in prompt",
lambda: run_gemini("What does `print()` do in Python?"),
lambda r: "output" in r.lower() or "print" in r.lower() or "console" in r.lower(),
timeout_sec=30)
# SUMMARY
total_tests = 40
print("\n" + "=" * 50)
print(f"RESULTS: {passed}/{total_tests} passed ({passed/total_tests*100:.0f}%)")
print(f"Passed: {passed} ✅ Failed: {failed} ❌")
print("=" * 50)
return failed == 0
if __name__ == "__main__":
success = main()
sys.exit(0 if success else 1)