feat: initial commit from workspace-mcp
Some checks failed
Check Maintainer Edits Enabled / check-maintainer-edits (pull_request) Has been cancelled
Check Maintainer Edits Enabled / check-maintainer-edits-internal (pull_request) Has been cancelled
Docker Build and Push to GHCR / build-and-push (pull_request) Has been cancelled
Ruff / ruff (pull_request) Has been cancelled
Some checks failed
Check Maintainer Edits Enabled / check-maintainer-edits (pull_request) Has been cancelled
Check Maintainer Edits Enabled / check-maintainer-edits-internal (pull_request) Has been cancelled
Docker Build and Push to GHCR / build-and-push (pull_request) Has been cancelled
Ruff / ruff (pull_request) Has been cancelled
This commit is contained in:
0
tests/gappsscript/__init__.py
Normal file
0
tests/gappsscript/__init__.py
Normal file
373
tests/gappsscript/manual_test.py
Normal file
373
tests/gappsscript/manual_test.py
Normal file
@@ -0,0 +1,373 @@
|
||||
"""
|
||||
Manual E2E test script for Apps Script integration.
|
||||
|
||||
This script tests Apps Script tools against the real Google API.
|
||||
Requires valid OAuth credentials and enabled Apps Script API.
|
||||
|
||||
Usage:
|
||||
python tests/gappsscript/manual_test.py
|
||||
|
||||
Environment Variables:
|
||||
GOOGLE_CLIENT_SECRET_PATH: Path to client_secret.json (default: ./client_secret.json)
|
||||
GOOGLE_TOKEN_PATH: Path to store OAuth token (default: ./test_token.pickle)
|
||||
|
||||
Note: This will create real Apps Script projects in your account.
|
||||
Delete test projects manually after running.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
import os
|
||||
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../..")))
|
||||
|
||||
from googleapiclient.discovery import build
|
||||
from google_auth_oauthlib.flow import InstalledAppFlow
|
||||
from google.auth.transport.requests import Request
|
||||
import pickle
|
||||
|
||||
|
||||
SCOPES = [
|
||||
"https://www.googleapis.com/auth/script.projects",
|
||||
"https://www.googleapis.com/auth/script.deployments",
|
||||
"https://www.googleapis.com/auth/script.processes",
|
||||
"https://www.googleapis.com/auth/drive.readonly", # For listing script projects
|
||||
"https://www.googleapis.com/auth/userinfo.email", # Basic user info
|
||||
"openid", # Required by Google OAuth
|
||||
]
|
||||
|
||||
# Allow http://localhost for OAuth (required for headless auth)
|
||||
os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"
|
||||
|
||||
# Default paths (can be overridden via environment variables)
|
||||
PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "../.."))
|
||||
DEFAULT_CLIENT_SECRET = os.path.join(PROJECT_ROOT, "client_secret.json")
|
||||
DEFAULT_TOKEN_PATH = os.path.join(PROJECT_ROOT, "test_token.pickle")
|
||||
|
||||
|
||||
def get_credentials():
|
||||
"""
|
||||
Get OAuth credentials for Apps Script API.
|
||||
|
||||
Credential paths can be configured via environment variables:
|
||||
- GOOGLE_CLIENT_SECRET_PATH: Path to client_secret.json
|
||||
- GOOGLE_TOKEN_PATH: Path to store/load OAuth token
|
||||
|
||||
Returns:
|
||||
Credentials object
|
||||
"""
|
||||
creds = None
|
||||
token_path = os.environ.get("GOOGLE_TOKEN_PATH", DEFAULT_TOKEN_PATH)
|
||||
client_secret_path = os.environ.get(
|
||||
"GOOGLE_CLIENT_SECRET_PATH", DEFAULT_CLIENT_SECRET
|
||||
)
|
||||
|
||||
if os.path.exists(token_path):
|
||||
with open(token_path, "rb") as token:
|
||||
creds = pickle.load(token)
|
||||
|
||||
if not creds or not creds.valid:
|
||||
if creds and creds.expired and creds.refresh_token:
|
||||
creds.refresh(Request())
|
||||
else:
|
||||
if not os.path.exists(client_secret_path):
|
||||
print(f"Error: {client_secret_path} not found")
|
||||
print("\nTo fix this:")
|
||||
print("1. Go to Google Cloud Console > APIs & Services > Credentials")
|
||||
print("2. Create an OAuth 2.0 Client ID (Desktop application type)")
|
||||
print("3. Download the JSON and save as client_secret.json")
|
||||
print(f"\nExpected path: {client_secret_path}")
|
||||
print("\nOr set GOOGLE_CLIENT_SECRET_PATH environment variable")
|
||||
sys.exit(1)
|
||||
|
||||
flow = InstalledAppFlow.from_client_secrets_file(client_secret_path, SCOPES)
|
||||
# Set redirect URI to match client_secret.json
|
||||
flow.redirect_uri = "http://localhost"
|
||||
# Headless flow: user copies redirect URL after auth
|
||||
auth_url, _ = flow.authorization_url(prompt="consent")
|
||||
print("\n" + "=" * 60)
|
||||
print("HEADLESS AUTH")
|
||||
print("=" * 60)
|
||||
print("\n1. Open this URL in any browser:\n")
|
||||
print(auth_url)
|
||||
print("\n2. Sign in and authorize the app")
|
||||
print("3. You'll be redirected to http://localhost (won't load)")
|
||||
print("4. Copy the FULL URL from browser address bar")
|
||||
print(" (looks like: http://localhost/?code=4/0A...&scope=...)")
|
||||
print("5. Paste it below:\n")
|
||||
redirect_response = input("Paste full redirect URL: ").strip()
|
||||
flow.fetch_token(authorization_response=redirect_response)
|
||||
creds = flow.credentials
|
||||
|
||||
with open(token_path, "wb") as token:
|
||||
pickle.dump(creds, token)
|
||||
|
||||
return creds
|
||||
|
||||
|
||||
async def test_list_projects(drive_service):
|
||||
"""Test listing Apps Script projects using Drive API"""
|
||||
print("\n=== Test: List Projects ===")
|
||||
|
||||
from gappsscript.apps_script_tools import _list_script_projects_impl
|
||||
|
||||
try:
|
||||
result = await _list_script_projects_impl(
|
||||
service=drive_service, user_google_email="test@example.com", page_size=10
|
||||
)
|
||||
print(result)
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
return False
|
||||
|
||||
|
||||
async def test_create_project(service):
|
||||
"""Test creating a new Apps Script project"""
|
||||
print("\n=== Test: Create Project ===")
|
||||
|
||||
from gappsscript.apps_script_tools import _create_script_project_impl
|
||||
|
||||
try:
|
||||
result = await _create_script_project_impl(
|
||||
service=service,
|
||||
user_google_email="test@example.com",
|
||||
title="MCP Test Project",
|
||||
)
|
||||
print(result)
|
||||
|
||||
if "Script ID:" in result:
|
||||
script_id = result.split("Script ID: ")[1].split("\n")[0]
|
||||
return script_id
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
return None
|
||||
|
||||
|
||||
async def test_get_project(service, script_id):
|
||||
"""Test retrieving project details"""
|
||||
print(f"\n=== Test: Get Project {script_id} ===")
|
||||
|
||||
from gappsscript.apps_script_tools import _get_script_project_impl
|
||||
|
||||
try:
|
||||
result = await _get_script_project_impl(
|
||||
service=service, user_google_email="test@example.com", script_id=script_id
|
||||
)
|
||||
print(result)
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
return False
|
||||
|
||||
|
||||
async def test_update_content(service, script_id):
|
||||
"""Test updating script content"""
|
||||
print(f"\n=== Test: Update Content {script_id} ===")
|
||||
|
||||
from gappsscript.apps_script_tools import _update_script_content_impl
|
||||
|
||||
files = [
|
||||
{
|
||||
"name": "appsscript",
|
||||
"type": "JSON",
|
||||
"source": """{
|
||||
"timeZone": "America/New_York",
|
||||
"dependencies": {},
|
||||
"exceptionLogging": "STACKDRIVER",
|
||||
"runtimeVersion": "V8"
|
||||
}""",
|
||||
},
|
||||
{
|
||||
"name": "Code",
|
||||
"type": "SERVER_JS",
|
||||
"source": """function testFunction() {
|
||||
Logger.log('Hello from MCP test!');
|
||||
return 'Test successful';
|
||||
}""",
|
||||
},
|
||||
]
|
||||
|
||||
try:
|
||||
result = await _update_script_content_impl(
|
||||
service=service,
|
||||
user_google_email="test@example.com",
|
||||
script_id=script_id,
|
||||
files=files,
|
||||
)
|
||||
print(result)
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
return False
|
||||
|
||||
|
||||
async def test_run_function(service, script_id):
|
||||
"""Test running a script function"""
|
||||
print(f"\n=== Test: Run Function {script_id} ===")
|
||||
|
||||
from gappsscript.apps_script_tools import _run_script_function_impl
|
||||
|
||||
try:
|
||||
result = await _run_script_function_impl(
|
||||
service=service,
|
||||
user_google_email="test@example.com",
|
||||
script_id=script_id,
|
||||
function_name="testFunction",
|
||||
dev_mode=True,
|
||||
)
|
||||
print(result)
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
return False
|
||||
|
||||
|
||||
async def test_create_deployment(service, script_id):
|
||||
"""Test creating a deployment"""
|
||||
print(f"\n=== Test: Create Deployment {script_id} ===")
|
||||
|
||||
from gappsscript.apps_script_tools import _create_deployment_impl
|
||||
|
||||
try:
|
||||
result = await _create_deployment_impl(
|
||||
service=service,
|
||||
user_google_email="test@example.com",
|
||||
script_id=script_id,
|
||||
description="MCP Test Deployment",
|
||||
)
|
||||
print(result)
|
||||
|
||||
if "Deployment ID:" in result:
|
||||
deployment_id = result.split("Deployment ID: ")[1].split("\n")[0]
|
||||
return deployment_id
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
return None
|
||||
|
||||
|
||||
async def test_list_deployments(service, script_id):
|
||||
"""Test listing deployments"""
|
||||
print(f"\n=== Test: List Deployments {script_id} ===")
|
||||
|
||||
from gappsscript.apps_script_tools import _list_deployments_impl
|
||||
|
||||
try:
|
||||
result = await _list_deployments_impl(
|
||||
service=service, user_google_email="test@example.com", script_id=script_id
|
||||
)
|
||||
print(result)
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
return False
|
||||
|
||||
|
||||
async def test_list_processes(service):
|
||||
"""Test listing script processes"""
|
||||
print("\n=== Test: List Processes ===")
|
||||
|
||||
from gappsscript.apps_script_tools import _list_script_processes_impl
|
||||
|
||||
try:
|
||||
result = await _list_script_processes_impl(
|
||||
service=service, user_google_email="test@example.com", page_size=10
|
||||
)
|
||||
print(result)
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
return False
|
||||
|
||||
|
||||
async def cleanup_test_project(service, script_id):
|
||||
"""
|
||||
Cleanup test project (requires Drive API).
|
||||
Note: Apps Script API does not have a delete endpoint.
|
||||
Projects must be deleted via Drive API by moving to trash.
|
||||
"""
|
||||
print(f"\n=== Cleanup: Delete Project {script_id} ===")
|
||||
print("Note: Apps Script projects must be deleted via Drive API")
|
||||
print(f"Please manually delete: https://script.google.com/d/{script_id}/edit")
|
||||
|
||||
|
||||
async def run_all_tests():
|
||||
"""Run all manual tests"""
|
||||
print("=" * 60)
|
||||
print("Apps Script MCP Manual Test Suite")
|
||||
print("=" * 60)
|
||||
|
||||
print("\nGetting OAuth credentials...")
|
||||
creds = get_credentials()
|
||||
|
||||
print("Building API services...")
|
||||
script_service = build("script", "v1", credentials=creds)
|
||||
drive_service = build("drive", "v3", credentials=creds)
|
||||
|
||||
test_script_id = None
|
||||
deployment_id = None
|
||||
|
||||
try:
|
||||
success = await test_list_projects(drive_service)
|
||||
if not success:
|
||||
print("\nWarning: List projects failed")
|
||||
|
||||
test_script_id = await test_create_project(script_service)
|
||||
if test_script_id:
|
||||
print(f"\nCreated test project: {test_script_id}")
|
||||
|
||||
await test_get_project(script_service, test_script_id)
|
||||
await test_update_content(script_service, test_script_id)
|
||||
|
||||
await asyncio.sleep(2)
|
||||
|
||||
await test_run_function(script_service, test_script_id)
|
||||
|
||||
deployment_id = await test_create_deployment(script_service, test_script_id)
|
||||
if deployment_id:
|
||||
print(f"\nCreated deployment: {deployment_id}")
|
||||
|
||||
await test_list_deployments(script_service, test_script_id)
|
||||
else:
|
||||
print("\nSkipping tests that require a project (creation failed)")
|
||||
|
||||
await test_list_processes(script_service)
|
||||
|
||||
finally:
|
||||
if test_script_id:
|
||||
await cleanup_test_project(script_service, test_script_id)
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("Manual Test Suite Complete")
|
||||
print("=" * 60)
|
||||
|
||||
|
||||
def main():
|
||||
"""Main entry point"""
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description="Manual E2E test for Apps Script")
|
||||
parser.add_argument(
|
||||
"--yes", "-y", action="store_true", help="Skip confirmation prompt"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
print("\nIMPORTANT: This script will:")
|
||||
print("1. Create a test Apps Script project in your account")
|
||||
print("2. Run various operations on it")
|
||||
print("3. Leave the project for manual cleanup")
|
||||
print("\nYou must manually delete the test project after running this.")
|
||||
|
||||
if not args.yes:
|
||||
response = input("\nContinue? (yes/no): ")
|
||||
if response.lower() not in ["yes", "y"]:
|
||||
print("Aborted")
|
||||
return
|
||||
|
||||
asyncio.run(run_all_tests())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
432
tests/gappsscript/test_apps_script_tools.py
Normal file
432
tests/gappsscript/test_apps_script_tools.py
Normal file
@@ -0,0 +1,432 @@
|
||||
"""
|
||||
Unit tests for Google Apps Script MCP tools
|
||||
|
||||
Tests all Apps Script tools with mocked API responses
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from unittest.mock import Mock
|
||||
import sys
|
||||
import os
|
||||
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../..")))
|
||||
|
||||
# Import the internal implementation functions (not the decorated ones)
|
||||
from gappsscript.apps_script_tools import (
|
||||
_list_script_projects_impl,
|
||||
_get_script_project_impl,
|
||||
_create_script_project_impl,
|
||||
_update_script_content_impl,
|
||||
_run_script_function_impl,
|
||||
_create_deployment_impl,
|
||||
_list_deployments_impl,
|
||||
_update_deployment_impl,
|
||||
_delete_deployment_impl,
|
||||
_list_script_processes_impl,
|
||||
_delete_script_project_impl,
|
||||
_list_versions_impl,
|
||||
_create_version_impl,
|
||||
_get_version_impl,
|
||||
_get_script_metrics_impl,
|
||||
_generate_trigger_code_impl,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_script_projects():
|
||||
"""Test listing Apps Script projects via Drive API"""
|
||||
mock_service = Mock()
|
||||
mock_response = {
|
||||
"files": [
|
||||
{
|
||||
"id": "test123",
|
||||
"name": "Test Project",
|
||||
"createdTime": "2025-01-10T10:00:00Z",
|
||||
"modifiedTime": "2026-01-12T15:30:00Z",
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
mock_service.files().list().execute.return_value = mock_response
|
||||
|
||||
result = await _list_script_projects_impl(
|
||||
service=mock_service, user_google_email="test@example.com", page_size=50
|
||||
)
|
||||
|
||||
assert "Found 1 Apps Script projects" in result
|
||||
assert "Test Project" in result
|
||||
assert "test123" in result
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_script_project():
|
||||
"""Test retrieving complete project details"""
|
||||
mock_service = Mock()
|
||||
|
||||
# projects().get() returns metadata only (no files)
|
||||
mock_metadata_response = {
|
||||
"scriptId": "test123",
|
||||
"title": "Test Project",
|
||||
"creator": {"email": "creator@example.com"},
|
||||
"createTime": "2025-01-10T10:00:00Z",
|
||||
"updateTime": "2026-01-12T15:30:00Z",
|
||||
}
|
||||
|
||||
# projects().getContent() returns files with source code
|
||||
mock_content_response = {
|
||||
"scriptId": "test123",
|
||||
"files": [
|
||||
{
|
||||
"name": "Code",
|
||||
"type": "SERVER_JS",
|
||||
"source": "function test() { return 'hello'; }",
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
mock_service.projects().get().execute.return_value = mock_metadata_response
|
||||
mock_service.projects().getContent().execute.return_value = mock_content_response
|
||||
|
||||
result = await _get_script_project_impl(
|
||||
service=mock_service, user_google_email="test@example.com", script_id="test123"
|
||||
)
|
||||
|
||||
assert "Test Project" in result
|
||||
assert "creator@example.com" in result
|
||||
assert "Code" in result
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_script_project():
|
||||
"""Test creating new Apps Script project"""
|
||||
mock_service = Mock()
|
||||
mock_response = {"scriptId": "new123", "title": "New Project"}
|
||||
|
||||
mock_service.projects().create().execute.return_value = mock_response
|
||||
|
||||
result = await _create_script_project_impl(
|
||||
service=mock_service, user_google_email="test@example.com", title="New Project"
|
||||
)
|
||||
|
||||
assert "Script ID: new123" in result
|
||||
assert "New Project" in result
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_script_content():
|
||||
"""Test updating script project files"""
|
||||
mock_service = Mock()
|
||||
files_to_update = [
|
||||
{"name": "Code", "type": "SERVER_JS", "source": "function main() {}"}
|
||||
]
|
||||
mock_response = {"files": files_to_update}
|
||||
|
||||
mock_service.projects().updateContent().execute.return_value = mock_response
|
||||
|
||||
result = await _update_script_content_impl(
|
||||
service=mock_service,
|
||||
user_google_email="test@example.com",
|
||||
script_id="test123",
|
||||
files=files_to_update,
|
||||
)
|
||||
|
||||
assert "Updated script project: test123" in result
|
||||
assert "Code" in result
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_run_script_function():
|
||||
"""Test executing script function"""
|
||||
mock_service = Mock()
|
||||
mock_response = {"response": {"result": "Success"}}
|
||||
|
||||
mock_service.scripts().run().execute.return_value = mock_response
|
||||
|
||||
result = await _run_script_function_impl(
|
||||
service=mock_service,
|
||||
user_google_email="test@example.com",
|
||||
script_id="test123",
|
||||
function_name="myFunction",
|
||||
dev_mode=True,
|
||||
)
|
||||
|
||||
assert "Execution successful" in result
|
||||
assert "myFunction" in result
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_deployment():
|
||||
"""Test creating deployment"""
|
||||
mock_service = Mock()
|
||||
|
||||
# Mock version creation (called first)
|
||||
mock_version_response = {"versionNumber": 1}
|
||||
mock_service.projects().versions().create().execute.return_value = (
|
||||
mock_version_response
|
||||
)
|
||||
|
||||
# Mock deployment creation (called second)
|
||||
mock_deploy_response = {
|
||||
"deploymentId": "deploy123",
|
||||
"deploymentConfig": {},
|
||||
}
|
||||
mock_service.projects().deployments().create().execute.return_value = (
|
||||
mock_deploy_response
|
||||
)
|
||||
|
||||
result = await _create_deployment_impl(
|
||||
service=mock_service,
|
||||
user_google_email="test@example.com",
|
||||
script_id="test123",
|
||||
description="Test deployment",
|
||||
)
|
||||
|
||||
assert "Deployment ID: deploy123" in result
|
||||
assert "Test deployment" in result
|
||||
assert "Version: 1" in result
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_deployments():
|
||||
"""Test listing deployments"""
|
||||
mock_service = Mock()
|
||||
mock_response = {
|
||||
"deployments": [
|
||||
{
|
||||
"deploymentId": "deploy123",
|
||||
"description": "Production",
|
||||
"updateTime": "2026-01-12T15:30:00Z",
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
mock_service.projects().deployments().list().execute.return_value = mock_response
|
||||
|
||||
result = await _list_deployments_impl(
|
||||
service=mock_service, user_google_email="test@example.com", script_id="test123"
|
||||
)
|
||||
|
||||
assert "Production" in result
|
||||
assert "deploy123" in result
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_deployment():
|
||||
"""Test updating deployment"""
|
||||
mock_service = Mock()
|
||||
mock_response = {
|
||||
"deploymentId": "deploy123",
|
||||
"description": "Updated description",
|
||||
}
|
||||
|
||||
mock_service.projects().deployments().update().execute.return_value = mock_response
|
||||
|
||||
result = await _update_deployment_impl(
|
||||
service=mock_service,
|
||||
user_google_email="test@example.com",
|
||||
script_id="test123",
|
||||
deployment_id="deploy123",
|
||||
description="Updated description",
|
||||
)
|
||||
|
||||
assert "Updated deployment: deploy123" in result
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_delete_deployment():
|
||||
"""Test deleting deployment"""
|
||||
mock_service = Mock()
|
||||
mock_service.projects().deployments().delete().execute.return_value = {}
|
||||
|
||||
result = await _delete_deployment_impl(
|
||||
service=mock_service,
|
||||
user_google_email="test@example.com",
|
||||
script_id="test123",
|
||||
deployment_id="deploy123",
|
||||
)
|
||||
|
||||
assert "Deleted deployment: deploy123 from script: test123" in result
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_script_processes():
|
||||
"""Test listing script processes"""
|
||||
mock_service = Mock()
|
||||
mock_response = {
|
||||
"processes": [
|
||||
{
|
||||
"functionName": "myFunction",
|
||||
"processStatus": "COMPLETED",
|
||||
"startTime": "2026-01-12T15:30:00Z",
|
||||
"duration": "5s",
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
mock_service.processes().list().execute.return_value = mock_response
|
||||
|
||||
result = await _list_script_processes_impl(
|
||||
service=mock_service, user_google_email="test@example.com", page_size=50
|
||||
)
|
||||
|
||||
assert "myFunction" in result
|
||||
assert "COMPLETED" in result
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_delete_script_project():
|
||||
"""Test deleting a script project"""
|
||||
mock_service = Mock()
|
||||
mock_service.files().delete().execute.return_value = {}
|
||||
|
||||
result = await _delete_script_project_impl(
|
||||
service=mock_service, user_google_email="test@example.com", script_id="test123"
|
||||
)
|
||||
|
||||
assert "Deleted Apps Script project: test123" in result
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_versions():
|
||||
"""Test listing script versions"""
|
||||
mock_service = Mock()
|
||||
mock_response = {
|
||||
"versions": [
|
||||
{
|
||||
"versionNumber": 1,
|
||||
"description": "Initial version",
|
||||
"createTime": "2025-01-10T10:00:00Z",
|
||||
},
|
||||
{
|
||||
"versionNumber": 2,
|
||||
"description": "Bug fix",
|
||||
"createTime": "2026-01-12T15:30:00Z",
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
mock_service.projects().versions().list().execute.return_value = mock_response
|
||||
|
||||
result = await _list_versions_impl(
|
||||
service=mock_service, user_google_email="test@example.com", script_id="test123"
|
||||
)
|
||||
|
||||
assert "Version 1" in result
|
||||
assert "Initial version" in result
|
||||
assert "Version 2" in result
|
||||
assert "Bug fix" in result
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_version():
|
||||
"""Test creating a new version"""
|
||||
mock_service = Mock()
|
||||
mock_response = {
|
||||
"versionNumber": 3,
|
||||
"createTime": "2026-01-13T10:00:00Z",
|
||||
}
|
||||
|
||||
mock_service.projects().versions().create().execute.return_value = mock_response
|
||||
|
||||
result = await _create_version_impl(
|
||||
service=mock_service,
|
||||
user_google_email="test@example.com",
|
||||
script_id="test123",
|
||||
description="New feature",
|
||||
)
|
||||
|
||||
assert "Created version 3" in result
|
||||
assert "New feature" in result
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_version():
|
||||
"""Test getting a specific version"""
|
||||
mock_service = Mock()
|
||||
mock_response = {
|
||||
"versionNumber": 2,
|
||||
"description": "Bug fix",
|
||||
"createTime": "2026-01-12T15:30:00Z",
|
||||
}
|
||||
|
||||
mock_service.projects().versions().get().execute.return_value = mock_response
|
||||
|
||||
result = await _get_version_impl(
|
||||
service=mock_service,
|
||||
user_google_email="test@example.com",
|
||||
script_id="test123",
|
||||
version_number=2,
|
||||
)
|
||||
|
||||
assert "Version 2" in result
|
||||
assert "Bug fix" in result
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_script_metrics():
|
||||
"""Test getting script metrics"""
|
||||
mock_service = Mock()
|
||||
mock_response = {
|
||||
"activeUsers": [
|
||||
{"startTime": "2026-01-01", "endTime": "2026-01-02", "value": "10"}
|
||||
],
|
||||
"totalExecutions": [
|
||||
{"startTime": "2026-01-01", "endTime": "2026-01-02", "value": "100"}
|
||||
],
|
||||
"failedExecutions": [
|
||||
{"startTime": "2026-01-01", "endTime": "2026-01-02", "value": "5"}
|
||||
],
|
||||
}
|
||||
|
||||
mock_service.projects().getMetrics().execute.return_value = mock_response
|
||||
|
||||
result = await _get_script_metrics_impl(
|
||||
service=mock_service,
|
||||
user_google_email="test@example.com",
|
||||
script_id="test123",
|
||||
metrics_granularity="DAILY",
|
||||
)
|
||||
|
||||
assert "Active Users" in result
|
||||
assert "10 users" in result
|
||||
assert "Total Executions" in result
|
||||
assert "100 executions" in result
|
||||
assert "Failed Executions" in result
|
||||
assert "5 failures" in result
|
||||
|
||||
|
||||
def test_generate_trigger_code_daily():
|
||||
"""Test generating daily trigger code"""
|
||||
result = _generate_trigger_code_impl(
|
||||
trigger_type="time_daily",
|
||||
function_name="sendReport",
|
||||
schedule="9",
|
||||
)
|
||||
|
||||
assert "INSTALLABLE TRIGGER" in result
|
||||
assert "createDailyTrigger_sendReport" in result
|
||||
assert "everyDays(1)" in result
|
||||
assert "atHour(9)" in result
|
||||
|
||||
|
||||
def test_generate_trigger_code_on_edit():
|
||||
"""Test generating onEdit trigger code"""
|
||||
result = _generate_trigger_code_impl(
|
||||
trigger_type="on_edit",
|
||||
function_name="processEdit",
|
||||
)
|
||||
|
||||
assert "SIMPLE TRIGGER" in result
|
||||
assert "function onEdit" in result
|
||||
assert "processEdit()" in result
|
||||
|
||||
|
||||
def test_generate_trigger_code_invalid():
|
||||
"""Test generating trigger code with invalid type"""
|
||||
result = _generate_trigger_code_impl(
|
||||
trigger_type="invalid_type",
|
||||
function_name="test",
|
||||
)
|
||||
|
||||
assert "Unknown trigger type" in result
|
||||
assert "Valid types:" in result
|
||||
Reference in New Issue
Block a user