From e41fc94c6a76fe5e56678cfe9ebb0d3ade6a4b99 Mon Sep 17 00:00:00 2001 From: Ivan Shymko Date: Fri, 30 Jan 2026 15:53:12 +0000 Subject: [PATCH 1/7] test: add script to run integration tests locally --- scripts/docker-compose.test.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 scripts/docker-compose.test.yml diff --git a/scripts/docker-compose.test.yml b/scripts/docker-compose.test.yml new file mode 100644 index 000000000..a2df936e1 --- /dev/null +++ b/scripts/docker-compose.test.yml @@ -0,0 +1,29 @@ +services: + postgres: + image: postgres:15-alpine + environment: + POSTGRES_USER: a2a + POSTGRES_PASSWORD: a2a_password + POSTGRES_DB: a2a_test + ports: + - "5432:5432" + healthcheck: + test: ["CMD-SHELL", "pg_isready"] + interval: 10s + timeout: 5s + retries: 5 + + mysql: + image: mysql:8.0 + environment: + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: a2a_test + MYSQL_USER: a2a + MYSQL_PASSWORD: a2a_password + ports: + - "3306:3306" + healthcheck: + test: ["CMD-SHELL", "mysqladmin ping -h localhost -u root -proot"] + interval: 10s + timeout: 5s + retries: 5 From ec0a7d1f853602efd7efd62e8c269ca565b4e5d4 Mon Sep 17 00:00:00 2001 From: Ivan Shymko Date: Fri, 30 Jan 2026 16:10:18 +0000 Subject: [PATCH 2/7] Rewrite in Python --- scripts/run_integration_tests.py | 117 +++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100755 scripts/run_integration_tests.py diff --git a/scripts/run_integration_tests.py b/scripts/run_integration_tests.py new file mode 100755 index 000000000..00a3390cc --- /dev/null +++ b/scripts/run_integration_tests.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 +import argparse +import os +import signal +import subprocess +import sys +from pathlib import Path + +# Constants +SCRIPT_DIR = Path(__file__).resolve().parent +PROJECT_ROOT = SCRIPT_DIR.parent +COMPOSE_FILE = SCRIPT_DIR / "docker-compose.test.yml" + +DSNS = { + "postgres": "postgresql+asyncpg://a2a:a2a_password@localhost:5432/a2a_test", + "mysql": "mysql+aiomysql://a2a:a2a_password@localhost:3306/a2a_test", +} + +def run_command(command, cwd=None, env=None, check=True): + """Runs a shell command.""" + try: + subprocess.run(command, cwd=cwd, env=env, check=check) + except subprocess.CalledProcessError as e: + sys.exit(e.returncode) + +def stop_databases(): + """Stops the test databases.""" + print("Stopping test databases...") + run_command( + ["docker", "compose", "-f", str(COMPOSE_FILE), "down"], + check=False + ) + +def main(): + parser = argparse.ArgumentParser( + description="Run integration tests with Docker databases.", + add_help=False # handle help manually to pass args to pytest + ) + parser.add_argument("--debug", action="store_true", help="Start DBs and exit without running tests") + parser.add_argument("--stop", action="store_true", help="Stop the databases and exit") + parser.add_argument("--postgres", action="store_true", help="Use PostgreSQL") + parser.add_argument("--mysql", action="store_true", help="Use MySQL") + parser.add_argument("--help", action="store_true", help="Show this help message") + + # Parse known args, leave the rest for pytest + args, pytest_args = parser.parse_known_args() + + if args.help: + parser.print_help() + print("\nAny other arguments will be passed to pytest.") + sys.exit(0) + + if args.stop: + stop_databases() + sys.exit(0) + + # Determine which services to run + services = [] + if args.postgres: + services.append("postgres") + if args.mysql: + services.append("mysql") + + # Default to both if neither is specified + if not services: + services = ["postgres", "mysql"] + + print(f"Starting/Verifying databases: {', '.join(services)}...") + run_command( + ["docker", "compose", "-f", str(COMPOSE_FILE), "up", "-d", "--wait"] + services + ) + + # Prepare environment variables + env = os.environ.copy() + for service in services: + if service == "postgres": + env["POSTGRES_TEST_DSN"] = DSNS["postgres"] + elif service == "mysql": + env["MYSQL_TEST_DSN"] = DSNS["mysql"] + + if args.debug: + print("-" * 51) + print("Debug mode enabled. Databases are running.") + print("You can connect to them using the following DSNs:") + if "postgres" in services: + print(f"Postgres: {DSNS['postgres']}") + if "mysql" in services: + print(f"MySQL: {DSNS['mysql']}") + print("-" * 51) + print(f"Run {sys.argv[0]} --stop to shut them down.") + sys.exit(0) + + # Register cleanup on normal exit or signals + def signal_handler(sig, frame): + stop_databases() + sys.exit(0) + + signal.signal(signal.SIGINT, signal_handler) + signal.signal(signal.SIGTERM, signal_handler) + + try: + print("Running integration tests...") + + test_files = [ + "tests/server/tasks/test_database_task_store.py", + "tests/server/tasks/test_database_push_notification_config_store.py", + ] + + cmd = ["uv", "run", "--extra", "all", "pytest", "-v"] + test_files + pytest_args + run_command(cmd, cwd=PROJECT_ROOT, env=env) + + finally: + # Always cleanup unless in debug mode (which exits earlier) + stop_databases() + +if __name__ == "__main__": + main() From 2aee10c400d2c1319d2fe64cc588ed584b261bcd Mon Sep 17 00:00:00 2001 From: Ivan Shymko Date: Fri, 30 Jan 2026 16:13:28 +0000 Subject: [PATCH 3/7] Revert "Rewrite in Python" This reverts commit ec0a7d1f853602efd7efd62e8c269ca565b4e5d4. --- scripts/run_integration_tests.py | 117 ------------------------------- 1 file changed, 117 deletions(-) delete mode 100755 scripts/run_integration_tests.py diff --git a/scripts/run_integration_tests.py b/scripts/run_integration_tests.py deleted file mode 100755 index 00a3390cc..000000000 --- a/scripts/run_integration_tests.py +++ /dev/null @@ -1,117 +0,0 @@ -#!/usr/bin/env python3 -import argparse -import os -import signal -import subprocess -import sys -from pathlib import Path - -# Constants -SCRIPT_DIR = Path(__file__).resolve().parent -PROJECT_ROOT = SCRIPT_DIR.parent -COMPOSE_FILE = SCRIPT_DIR / "docker-compose.test.yml" - -DSNS = { - "postgres": "postgresql+asyncpg://a2a:a2a_password@localhost:5432/a2a_test", - "mysql": "mysql+aiomysql://a2a:a2a_password@localhost:3306/a2a_test", -} - -def run_command(command, cwd=None, env=None, check=True): - """Runs a shell command.""" - try: - subprocess.run(command, cwd=cwd, env=env, check=check) - except subprocess.CalledProcessError as e: - sys.exit(e.returncode) - -def stop_databases(): - """Stops the test databases.""" - print("Stopping test databases...") - run_command( - ["docker", "compose", "-f", str(COMPOSE_FILE), "down"], - check=False - ) - -def main(): - parser = argparse.ArgumentParser( - description="Run integration tests with Docker databases.", - add_help=False # handle help manually to pass args to pytest - ) - parser.add_argument("--debug", action="store_true", help="Start DBs and exit without running tests") - parser.add_argument("--stop", action="store_true", help="Stop the databases and exit") - parser.add_argument("--postgres", action="store_true", help="Use PostgreSQL") - parser.add_argument("--mysql", action="store_true", help="Use MySQL") - parser.add_argument("--help", action="store_true", help="Show this help message") - - # Parse known args, leave the rest for pytest - args, pytest_args = parser.parse_known_args() - - if args.help: - parser.print_help() - print("\nAny other arguments will be passed to pytest.") - sys.exit(0) - - if args.stop: - stop_databases() - sys.exit(0) - - # Determine which services to run - services = [] - if args.postgres: - services.append("postgres") - if args.mysql: - services.append("mysql") - - # Default to both if neither is specified - if not services: - services = ["postgres", "mysql"] - - print(f"Starting/Verifying databases: {', '.join(services)}...") - run_command( - ["docker", "compose", "-f", str(COMPOSE_FILE), "up", "-d", "--wait"] + services - ) - - # Prepare environment variables - env = os.environ.copy() - for service in services: - if service == "postgres": - env["POSTGRES_TEST_DSN"] = DSNS["postgres"] - elif service == "mysql": - env["MYSQL_TEST_DSN"] = DSNS["mysql"] - - if args.debug: - print("-" * 51) - print("Debug mode enabled. Databases are running.") - print("You can connect to them using the following DSNs:") - if "postgres" in services: - print(f"Postgres: {DSNS['postgres']}") - if "mysql" in services: - print(f"MySQL: {DSNS['mysql']}") - print("-" * 51) - print(f"Run {sys.argv[0]} --stop to shut them down.") - sys.exit(0) - - # Register cleanup on normal exit or signals - def signal_handler(sig, frame): - stop_databases() - sys.exit(0) - - signal.signal(signal.SIGINT, signal_handler) - signal.signal(signal.SIGTERM, signal_handler) - - try: - print("Running integration tests...") - - test_files = [ - "tests/server/tasks/test_database_task_store.py", - "tests/server/tasks/test_database_push_notification_config_store.py", - ] - - cmd = ["uv", "run", "--extra", "all", "pytest", "-v"] + test_files + pytest_args - run_command(cmd, cwd=PROJECT_ROOT, env=env) - - finally: - # Always cleanup unless in debug mode (which exits earlier) - stop_databases() - -if __name__ == "__main__": - main() From e62a0cfa3aec7e12407f12d5f179850c1ea25faa Mon Sep 17 00:00:00 2001 From: Ivan Shymko Date: Fri, 30 Jan 2026 16:15:30 +0000 Subject: [PATCH 4/7] Spelling --- .github/actions/spelling/allow.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/actions/spelling/allow.txt b/.github/actions/spelling/allow.txt index 11496c9f7..8d0b13c8c 100644 --- a/.github/actions/spelling/allow.txt +++ b/.github/actions/spelling/allow.txt @@ -63,6 +63,7 @@ Llm lstrips mikeas mockurl +mysqladmin notif oauthoidc oidc @@ -71,6 +72,7 @@ otherurl postgres POSTGRES postgresql +proot protoc pyi pypistats From a28bef75886be1494ee85fe91241775d0ae1cae8 Mon Sep 17 00:00:00 2001 From: Ivan Shymko Date: Fri, 30 Jan 2026 16:18:05 +0000 Subject: [PATCH 5/7] Add script back --- scripts/run_integration_tests.sh | 98 ++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 scripts/run_integration_tests.sh diff --git a/scripts/run_integration_tests.sh b/scripts/run_integration_tests.sh new file mode 100644 index 000000000..f71ad5fc7 --- /dev/null +++ b/scripts/run_integration_tests.sh @@ -0,0 +1,98 @@ +#!/bin/bash +set -e + +# Get the directory of this script +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" + +# Docker compose file path +COMPOSE_FILE="$SCRIPT_DIR/docker-compose.test.yml" + +# Initialize variables +DEBUG_MODE=false +STOP_MODE=false +SERVICES=() +PYTEST_ARGS=() + +# Parse arguments +while [[ $# -gt 0 ]]; do + case $1 in + --debug) + DEBUG_MODE=true + shift + ;; + --stop) + STOP_MODE=true + shift + ;; + --postgres) + SERVICES+=("postgres") + shift + ;; + --mysql) + SERVICES+=("mysql") + shift + ;; + *) + # Preserve other arguments for pytest + PYTEST_ARGS+=("$1") + shift + ;; + esac +done + +# Handle --stop +if [[ "$STOP_MODE" == "true" ]]; then + echo "Stopping test databases..." + docker compose -f "$COMPOSE_FILE" down + exit 0 +fi + +# Default to running both databases if none specified +if [[ ${#SERVICES[@]} -eq 0 ]]; then + SERVICES=("postgres" "mysql") +fi + +# Cleanup function to stop docker containers +cleanup() { + echo "Stopping test databases..." + docker compose -f "$COMPOSE_FILE" down +} + +# Start the databases +echo "Starting/Verifying databases: ${SERVICES[*]}..." +docker compose -f "$COMPOSE_FILE" up -d --wait "${SERVICES[@]}" + +# Set up environment variables based on active services +# Only export DSNs for started services so tests skip missing ones +for service in "${SERVICES[@]}"; do + if [[ "$service" == "postgres" ]]; then + export POSTGRES_TEST_DSN="postgresql+asyncpg://a2a:a2a_password@localhost:5432/a2a_test" + elif [[ "$service" == "mysql" ]]; then + export MYSQL_TEST_DSN="mysql+aiomysql://a2a:a2a_password@localhost:3306/a2a_test" + fi +done + +# Handle --debug mode +if [[ "$DEBUG_MODE" == "true" ]]; then + echo "---------------------------------------------------" + echo "Debug mode enabled. Databases are running." + echo "You can connect to them using the following DSNs:" + [[ -n "$POSTGRES_TEST_DSN" ]] && echo "Postgres: $POSTGRES_TEST_DSN" + [[ -n "$MYSQL_TEST_DSN" ]] && echo "MySQL: $MYSQL_TEST_DSN" + echo "---------------------------------------------------" + echo "Run ./scripts/run_integration_tests.sh --stop to shut them down." + exit 0 +fi + +# Register cleanup trap for normal test run +trap cleanup EXIT + +# Run the tests +echo "Running integration tests..." +cd "$PROJECT_ROOT" + +uv run --extra all pytest -v \ + tests/server/tasks/test_database_task_store.py \ + tests/server/tasks/test_database_push_notification_config_store.py \ + "${PYTEST_ARGS[@]}" From babbe548060ce92e33f8fab558ab560efb32ed91 Mon Sep 17 00:00:00 2001 From: Ivan Shymko Date: Fri, 30 Jan 2026 16:18:51 +0000 Subject: [PATCH 6/7] +x --- scripts/run_integration_tests.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 scripts/run_integration_tests.sh diff --git a/scripts/run_integration_tests.sh b/scripts/run_integration_tests.sh old mode 100644 new mode 100755 From 44face018b9437fd301dba1e0a4546c8f7786c45 Mon Sep 17 00:00:00 2001 From: Ivan Shymko Date: Fri, 30 Jan 2026 16:29:09 +0000 Subject: [PATCH 7/7] Env vars in debug mode --- scripts/run_integration_tests.sh | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/scripts/run_integration_tests.sh b/scripts/run_integration_tests.sh index f71ad5fc7..5b9767136 100755 --- a/scripts/run_integration_tests.sh +++ b/scripts/run_integration_tests.sh @@ -77,11 +77,15 @@ done if [[ "$DEBUG_MODE" == "true" ]]; then echo "---------------------------------------------------" echo "Debug mode enabled. Databases are running." - echo "You can connect to them using the following DSNs:" - [[ -n "$POSTGRES_TEST_DSN" ]] && echo "Postgres: $POSTGRES_TEST_DSN" - [[ -n "$MYSQL_TEST_DSN" ]] && echo "MySQL: $MYSQL_TEST_DSN" + echo "You can connect to them using the following DSNs." + echo "" + echo "Run the following commands to set up your environment:" + echo "" + [[ -n "$POSTGRES_TEST_DSN" ]] && echo "export POSTGRES_TEST_DSN=\"$POSTGRES_TEST_DSN\"" + [[ -n "$MYSQL_TEST_DSN" ]] && echo "export MYSQL_TEST_DSN=\"$MYSQL_TEST_DSN\"" + echo "" echo "---------------------------------------------------" - echo "Run ./scripts/run_integration_tests.sh --stop to shut them down." + echo "Run ./scripts/run_integration_tests.sh --stop to shut databases down." exit 0 fi