From 2460988a9291cbc759f54b037d0d05f77f210a1c Mon Sep 17 00:00:00 2001 From: Sai Sunder Srinivasan Date: Fri, 20 Dec 2024 07:54:27 +0000 Subject: [PATCH 1/3] fix: prototype conformance testing --- .kokoro/conformance-tests.sh | 69 +++++++++++++++++++++++++++++++++ conformance-tests/user_tests.py | 40 +++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100755 .kokoro/conformance-tests.sh create mode 100644 conformance-tests/user_tests.py diff --git a/.kokoro/conformance-tests.sh b/.kokoro/conformance-tests.sh new file mode 100755 index 000000000..c6a5b0bda --- /dev/null +++ b/.kokoro/conformance-tests.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +set -euo pipefail + +# Docker image to pull (your test "server" image) +readonly TEST_IMAGE="gcr.io/client-debugging/conformance_test_environment_0:conformance_test_environment_v2" + +# Project root +readonly PROJECT_ROOT="$(git rev-parse --show-toplevel)" + +# Test directory at the project root +readonly TEST_DIR="conformance-tests" + +# Check if the test directory exists +if [[ ! -d "${PROJECT_ROOT}/${TEST_DIR}" ]]; then + echo "ERROR: Test directory '${TEST_DIR}' not found. Aborting." >&2 + exit 1 +fi + +docker pull "${TEST_IMAGE}" || { # Pull the docker image. Exit if it fails. + echo "ERROR: Failed to pull Docker image. Aborting." >&2 + exit 1 +} + + +# Start the Docker container (detached mode, expose port 5000) +container_id=$(docker run -d -p 5007:5000 "${TEST_IMAGE}") || { + echo "ERROR: Failed to start Docker container. Aborting." >&2 + exit 1 +} + +cleanup() { # Define the cleanup function + docker stop "${container_id}" || true # Stop; ignore errors if already stopped + docker rm "${container_id}" || true # Remove; ignore errors if already removed +} + +trap cleanup EXIT # Ensure cleanup on ANY exit (normal or error) + +# Wait for the container's port to be open (adjust timeout as needed) +for i in {1..30}; do # Try for 30 seconds + if docker exec "${container_id}" bash -c 'nc -z localhost 5000'; then + echo "Container port 5000 is open. Proceeding with tests..." + break # Port is open, exit the loop + fi + sleep 1 +done + + +if [[ $i -eq 30 ]]; then # timeout reached + echo "ERROR: Timeout waiting for container port 5000 to open. Aborting." >&2 + docker stop "${container_id}" # Stop the container on error + docker rm "${container_id}" + exit 1 +fi + + +# Run your tests (outside the container, against localhost:5000) +python3 -m pip install --user pytest # Install pytest if needed +cd "${PROJECT_ROOT}/${TEST_DIR}" +python3 -m pytest user_tests.py # Or any other test runner + +# Capture the exit code of the test run +exit_code=$? + +# Stop and remove the container +docker stop "${container_id}" +docker rm "${container_id}" + +exit "${exit_code}" diff --git a/conformance-tests/user_tests.py b/conformance-tests/user_tests.py new file mode 100644 index 000000000..ccdbb7044 --- /dev/null +++ b/conformance-tests/user_tests.py @@ -0,0 +1,40 @@ +import pytest +import requests +import time +import os +import subprocess + +# @pytest.fixture(scope="session", autouse=True) +# def start_docker_container(): +# # Check if the container is already running +# container_id = os.popen("docker ps -q -f name=conformance_test").read().strip() +# if not container_id: # Start only if not running +# docker_image = os.environ.get('DOCKER_IMAGE', 'conformance_test_environment') +# command = f"docker run -d -p 5000:5000 --name conformance_test {docker_image}" +# subprocess.run(command, shell=True, check=True) # Use subprocess for better error handling +# time.sleep(5) # Give the container time to start + +# yield # This is where the tests run + +# # Teardown: Stop the container after all tests have finished (for CI) +# subprocess.run("docker stop conformance_test", shell=True, check=True) +# subprocess.run("docker rm conformance_test", shell=True, check=True) + + +def test_valid_token_refresh(): + url = "http://localhost:5007/oauth2/token200" + data = { + 'grant_type': 'refresh_token', + 'client_id': 'conformance_client', + 'client_secret': 'conformance_client_secret', + 'refresh_token': 'mock_refresh_token' + } + response = requests.post(url, data=data) + assert response.status_code == 200 + assert response.json()['access_token'] == 'mock_access_token' + assert response.json()['token_type'] == 'bearer' + assert response.json()['refresh_token'] == 'mock_refresh_token' + + + + From e3ecbe5704545071cb4ca93e864dee3d2cf91313 Mon Sep 17 00:00:00 2001 From: Sai Sunder Srinivasan Date: Fri, 20 Dec 2024 22:06:10 +0000 Subject: [PATCH 2/3] validation --- .kokoro/conformance-tests.sh | 7 ++- .../data/authorized_user_200.json | 9 +++ conformance-tests/user_tests.py | 56 +++++++++---------- 3 files changed, 38 insertions(+), 34 deletions(-) create mode 100644 conformance-tests/data/authorized_user_200.json diff --git a/.kokoro/conformance-tests.sh b/.kokoro/conformance-tests.sh index c6a5b0bda..9c90d3586 100755 --- a/.kokoro/conformance-tests.sh +++ b/.kokoro/conformance-tests.sh @@ -23,8 +23,9 @@ docker pull "${TEST_IMAGE}" || { # Pull the docker image. Exit if it fails. } -# Start the Docker container (detached mode, expose port 5000) -container_id=$(docker run -d -p 5007:5000 "${TEST_IMAGE}") || { +# Start the Docker container (detached mode, expose port) +PORT=5007 # Set the desired port here +container_id=$(docker run -d -p "${PORT}":5000 "${TEST_IMAGE}") || { echo "ERROR: Failed to start Docker container. Aborting." >&2 exit 1 } @@ -57,7 +58,7 @@ fi # Run your tests (outside the container, against localhost:5000) python3 -m pip install --user pytest # Install pytest if needed cd "${PROJECT_ROOT}/${TEST_DIR}" -python3 -m pytest user_tests.py # Or any other test runner +PORT="${PORT}" python3 -m pytest user_tests.py # Or any other test runner # Capture the exit code of the test run exit_code=$? diff --git a/conformance-tests/data/authorized_user_200.json b/conformance-tests/data/authorized_user_200.json new file mode 100644 index 000000000..0b94b80f7 --- /dev/null +++ b/conformance-tests/data/authorized_user_200.json @@ -0,0 +1,9 @@ +{ + "client_id": "your_client_id", + "client_secret": "your_client_secret", + "refresh_token": "your_refresh_token", + "token_uri": "http://localhost:{port}/oauth2/token200", + "scopes": ["email", "profile"] +} + + \ No newline at end of file diff --git a/conformance-tests/user_tests.py b/conformance-tests/user_tests.py index ccdbb7044..c8a19c131 100644 --- a/conformance-tests/user_tests.py +++ b/conformance-tests/user_tests.py @@ -1,40 +1,34 @@ import pytest -import requests -import time +from google.auth.transport.requests import Request import os -import subprocess - -# @pytest.fixture(scope="session", autouse=True) -# def start_docker_container(): -# # Check if the container is already running -# container_id = os.popen("docker ps -q -f name=conformance_test").read().strip() -# if not container_id: # Start only if not running -# docker_image = os.environ.get('DOCKER_IMAGE', 'conformance_test_environment') -# command = f"docker run -d -p 5000:5000 --name conformance_test {docker_image}" -# subprocess.run(command, shell=True, check=True) # Use subprocess for better error handling -# time.sleep(5) # Give the container time to start - -# yield # This is where the tests run +import requests +from google.oauth2 import credentials -# # Teardown: Stop the container after all tests have finished (for CI) -# subprocess.run("docker stop conformance_test", shell=True, check=True) -# subprocess.run("docker rm conformance_test", shell=True, check=True) +def test_valid_token_refresh(): + port = os.environ.get("PORT", "5000") # Get port from environment, default to 5000 + url = f"http://localhost:{port}/oauth2/token200" + # Create a Credentials object (replace with your actual refresh token) + creds = credentials.Credentials( + token=None, # No initial token + refresh_token='mock_refresh_token', # Your refresh token + token_uri=url, # Token endpoint + client_id='conformance_client', # Your client ID + client_secret='conformance_client_secret', # Your client secret + scopes=['email', 'profile'], # Your scopes, if needed (might not be for refresh) + ) -def test_valid_token_refresh(): - url = "http://localhost:5007/oauth2/token200" - data = { - 'grant_type': 'refresh_token', - 'client_id': 'conformance_client', - 'client_secret': 'conformance_client_secret', - 'refresh_token': 'mock_refresh_token' - } - response = requests.post(url, data=data) - assert response.status_code == 200 - assert response.json()['access_token'] == 'mock_access_token' - assert response.json()['token_type'] == 'bearer' - assert response.json()['refresh_token'] == 'mock_refresh_token' + # Refresh the credentials to get a new access token + try: + creds.refresh(Request()) # Refresh the token + except Exception as e: + pytest.fail(f"Token refresh failed: {e}") + headers = {} + creds.apply(headers) + url = f"http://localhost:{port}/oauth2/validate" # Make sure your server is running on this address and port + response = requests.post(url, headers=headers) + assert response.status_code == 200 \ No newline at end of file From 45f28e022adfc80a6f044daef6f8efe0c0d412a7 Mon Sep 17 00:00:00 2001 From: Sai Sunder Srinivasan Date: Thu, 16 Jan 2025 01:10:35 +0000 Subject: [PATCH 3/3] tried reading from file --- conformance-tests/data/authorized_user_200.json | 8 ++++---- conformance-tests/user_tests.py | 4 ++++ google/oauth2/credentials.py | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/conformance-tests/data/authorized_user_200.json b/conformance-tests/data/authorized_user_200.json index 0b94b80f7..910e0acf7 100644 --- a/conformance-tests/data/authorized_user_200.json +++ b/conformance-tests/data/authorized_user_200.json @@ -1,8 +1,8 @@ { - "client_id": "your_client_id", - "client_secret": "your_client_secret", - "refresh_token": "your_refresh_token", - "token_uri": "http://localhost:{port}/oauth2/token200", + "client_id": "conformance_client", + "client_secret": "conformance_client_secret", + "refresh_token": "mock_refresh_token", + "token_uri": "http://localhost:5000/oauth2/token200", "scopes": ["email", "profile"] } diff --git a/conformance-tests/user_tests.py b/conformance-tests/user_tests.py index c8a19c131..90619ac5b 100644 --- a/conformance-tests/user_tests.py +++ b/conformance-tests/user_tests.py @@ -4,6 +4,8 @@ import requests from google.oauth2 import credentials +DATA_DIR = os.path.join(os.path.dirname(__file__), "data") + def test_valid_token_refresh(): port = os.environ.get("PORT", "5000") # Get port from environment, default to 5000 url = f"http://localhost:{port}/oauth2/token200" @@ -18,6 +20,8 @@ def test_valid_token_refresh(): scopes=['email', 'profile'], # Your scopes, if needed (might not be for refresh) ) + # json_file_path = os.path.join(DATA_DIR, "authorized_user_200.json") + # creds = credentials.Credentials.from_authorized_user_file(json_file_path) # Refresh the credentials to get a new access token try: diff --git a/google/oauth2/credentials.py b/google/oauth2/credentials.py index 6e158089f..7cebf1832 100644 --- a/google/oauth2/credentials.py +++ b/google/oauth2/credentials.py @@ -484,7 +484,7 @@ def from_authorized_user_info(cls, info, scopes=None): return cls( token=info.get("token"), refresh_token=info.get("refresh_token"), - token_uri=_GOOGLE_OAUTH2_TOKEN_ENDPOINT, # always overrides + token_uri=info.get("token_uri"), # always overrides scopes=scopes, client_id=info.get("client_id"), client_secret=info.get("client_secret"),