4b6817381b
Benchmark image — build + push to ECR (any adapter) / build + push (push) Waiting to run
CI / quality (ubuntu-latest) (push) Waiting to run
CI / test (tools-runtime) (push) Waiting to run
CI / test (e2e-general) (push) Waiting to run
CI / test (cli-runtime) (push) Waiting to run
CI / test (e2e-provider-and-openclaw) (push) Waiting to run
CI / test (integrations-and-misc) (push) Waiting to run
CI / coverage-report (push) Blocked by required conditions
CI / test-kubernetes (push) Waiting to run
CI / should-run-thorough (push) Waiting to run
CI / test-thorough (cloudwatch-demo) (push) Blocked by required conditions
CI / test-thorough (flink-ecs) (push) Blocked by required conditions
CI / test-thorough (upstream-lambda) (push) Blocked by required conditions
CI / test-thorough (prefect-ecs-fargate) (push) Blocked by required conditions
CodeQL / Analyze (python) (push) Waiting to run
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Blocked by required conditions
Release / publish-release (push) Blocked by required conditions
Release / publish-main-release (push) Blocked by required conditions
Release / prepare (push) Waiting to run
Release / verify (push) Blocked by required conditions
Release / build-python-dist (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Blocked by required conditions
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Waiting to run
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Waiting to run
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Waiting to run
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
697 lines
26 KiB
Python
697 lines
26 KiB
Python
"""Unit tests for Google Docs integration client and tool."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from integrations.google_docs.client import (
|
|
GoogleDocsClient,
|
|
GoogleDocsIntegrationConfig,
|
|
build_google_docs_client_from_env,
|
|
)
|
|
from integrations.google_docs.tools import create_google_docs_incident_report
|
|
|
|
|
|
class TestGoogleDocsIntegrationConfig:
|
|
"""Unit tests for GoogleDocsIntegrationConfig model."""
|
|
|
|
def test_valid_config(self) -> None:
|
|
"""Test valid config creation."""
|
|
config = GoogleDocsIntegrationConfig(
|
|
credentials_file="/path/to/credentials.json",
|
|
folder_id="folder123",
|
|
)
|
|
assert config.credentials_file == "/path/to/credentials.json"
|
|
assert config.folder_id == "folder123"
|
|
|
|
def test_config_with_integration_id(self) -> None:
|
|
"""Test config with integration_id."""
|
|
config = GoogleDocsIntegrationConfig(
|
|
credentials_file="/path/to/credentials.json",
|
|
folder_id="folder123",
|
|
integration_id="google-docs-1",
|
|
)
|
|
assert config.integration_id == "google-docs-1"
|
|
|
|
def test_config_normalizes_whitespace(self) -> None:
|
|
"""Test that credentials_file is normalized."""
|
|
config = GoogleDocsIntegrationConfig(
|
|
credentials_file=" /path/to/credentials.json ",
|
|
folder_id="folder123",
|
|
)
|
|
assert config.credentials_file == "/path/to/credentials.json"
|
|
|
|
|
|
class TestGoogleDocsClient:
|
|
"""Unit tests for GoogleDocsClient."""
|
|
|
|
def test_is_configured_when_file_exists(self, tmp_path: Path) -> None:
|
|
"""Test is_configured returns True when credentials file exists."""
|
|
creds_file = tmp_path / "credentials.json"
|
|
creds_file.write_text('{"test": "content"}')
|
|
|
|
config = GoogleDocsIntegrationConfig(
|
|
credentials_file=str(creds_file),
|
|
folder_id="folder123",
|
|
)
|
|
client = GoogleDocsClient(config)
|
|
assert client.is_configured is True
|
|
|
|
def test_is_configured_when_file_missing(self) -> None:
|
|
"""Test is_configured returns False when credentials file doesn't exist."""
|
|
config = GoogleDocsIntegrationConfig(
|
|
credentials_file="/nonexistent/path/credentials.json",
|
|
folder_id="folder123",
|
|
)
|
|
client = GoogleDocsClient(config)
|
|
assert client.is_configured is False
|
|
|
|
def test_is_configured_when_folder_id_missing(self, tmp_path: Path) -> None:
|
|
"""Test is_configured returns False when folder_id is empty."""
|
|
creds_file = tmp_path / "credentials.json"
|
|
creds_file.write_text('{"test": "content"}')
|
|
|
|
config = GoogleDocsIntegrationConfig(
|
|
credentials_file=str(creds_file),
|
|
folder_id="",
|
|
)
|
|
client = GoogleDocsClient(config)
|
|
assert client.is_configured is False
|
|
|
|
def test_create_document_no_folder_id(self) -> None:
|
|
"""Test create_document fails when no folder_id provided or configured."""
|
|
config = GoogleDocsIntegrationConfig(
|
|
credentials_file="/path/to/creds.json",
|
|
folder_id="",
|
|
)
|
|
client = GoogleDocsClient(config)
|
|
result = client.create_document("Test Report")
|
|
assert result["success"] is False
|
|
assert "No folder_id" in result["error"]
|
|
|
|
def test_create_document_success(self, tmp_path: Path) -> None:
|
|
"""Test successful document creation."""
|
|
creds_file = tmp_path / "credentials.json"
|
|
creds_file.write_text('{"test": "content"}')
|
|
|
|
config = GoogleDocsIntegrationConfig(
|
|
credentials_file=str(creds_file),
|
|
folder_id="folder123",
|
|
)
|
|
client = GoogleDocsClient(config)
|
|
|
|
# Mock the _get_services method
|
|
mock_drive_service = MagicMock()
|
|
mock_files = MagicMock()
|
|
mock_drive_service.files.return_value = mock_files
|
|
mock_create = MagicMock()
|
|
mock_files.create.return_value = mock_create
|
|
mock_create.execute.return_value = {"id": "doc123", "name": "Test Report"}
|
|
|
|
with patch.object(client, "_get_services", return_value=(None, mock_drive_service)):
|
|
result = client.create_document("Test Report")
|
|
|
|
assert result["success"] is True
|
|
assert result["document_id"] == "doc123"
|
|
assert result["title"] == "Test Report"
|
|
assert result["document_url"] == "https://docs.google.com/document/d/doc123/edit"
|
|
|
|
def test_create_document_api_error(self, tmp_path: Path) -> None:
|
|
"""Test document creation with API error."""
|
|
creds_file = tmp_path / "credentials.json"
|
|
creds_file.write_text('{"test": "content"}')
|
|
|
|
config = GoogleDocsIntegrationConfig(
|
|
credentials_file=str(creds_file),
|
|
folder_id="folder123",
|
|
)
|
|
client = GoogleDocsClient(config)
|
|
|
|
with patch.object(client, "_get_services", side_effect=Exception("API Error")):
|
|
result = client.create_document("Test Report")
|
|
|
|
assert result["success"] is False
|
|
# Error message now comes from _handle_google_api_error
|
|
assert "API Error" in result["error"]
|
|
|
|
def test_validate_access_success(self, tmp_path: Path) -> None:
|
|
"""Test successful validation."""
|
|
creds_file = tmp_path / "credentials.json"
|
|
creds_file.write_text('{"test": "content"}')
|
|
|
|
config = GoogleDocsIntegrationConfig(
|
|
credentials_file=str(creds_file),
|
|
folder_id="folder123",
|
|
)
|
|
client = GoogleDocsClient(config)
|
|
|
|
# Mock the Drive service
|
|
mock_drive_service = MagicMock()
|
|
mock_files = MagicMock()
|
|
mock_drive_service.files.return_value = mock_files
|
|
|
|
# Mock for _validate_folder_exists (get folder info)
|
|
mock_get = MagicMock()
|
|
mock_get.execute.return_value = {
|
|
"id": "folder123",
|
|
"name": "Incident Reports",
|
|
"mimeType": "application/vnd.google-apps.folder",
|
|
}
|
|
mock_files.get.return_value = mock_get
|
|
|
|
# Mock for listing folder contents
|
|
mock_list = MagicMock()
|
|
mock_files.list.return_value = mock_list
|
|
mock_list.execute.return_value = {
|
|
"files": [
|
|
{"id": "1", "name": "file1", "mimeType": "application/pdf"},
|
|
{"id": "2", "name": "file2", "mimeType": "application/vnd.google-apps.document"},
|
|
]
|
|
}
|
|
|
|
with patch.object(client, "_get_services", return_value=(None, mock_drive_service)):
|
|
result = client.validate_access()
|
|
|
|
assert result["success"] is True
|
|
assert result["folder_id"] == "folder123"
|
|
assert result["folder_name"] == "Incident Reports"
|
|
assert result["file_count"] == 2
|
|
|
|
def test_probe_access_success(self, tmp_path: Path) -> None:
|
|
"""Test probe_access returns a passed probe result for a valid folder."""
|
|
creds_file = tmp_path / "credentials.json"
|
|
creds_file.write_text('{"test": "content"}')
|
|
|
|
config = GoogleDocsIntegrationConfig(
|
|
credentials_file=str(creds_file),
|
|
folder_id="folder123",
|
|
)
|
|
client = GoogleDocsClient(config)
|
|
|
|
with patch.object(
|
|
client, "validate_access", return_value={"success": True, "file_count": 2}
|
|
):
|
|
result = client.probe_access()
|
|
|
|
assert result.status == "passed"
|
|
assert "folder123" in result.detail
|
|
assert "2 items" in result.detail
|
|
|
|
def test_share_document_success(self, tmp_path: Path) -> None:
|
|
"""Test successful document sharing."""
|
|
creds_file = tmp_path / "credentials.json"
|
|
creds_file.write_text('{"test": "content"}')
|
|
|
|
config = GoogleDocsIntegrationConfig(
|
|
credentials_file=str(creds_file),
|
|
folder_id="folder123",
|
|
)
|
|
client = GoogleDocsClient(config)
|
|
|
|
# Mock the Drive service
|
|
mock_drive_service = MagicMock()
|
|
mock_permissions = MagicMock()
|
|
mock_drive_service.permissions.return_value = mock_permissions
|
|
mock_create = MagicMock()
|
|
mock_permissions.create.return_value = mock_create
|
|
mock_create.execute.return_value = {"id": "perm123"}
|
|
|
|
with patch.object(client, "_get_services", return_value=(None, mock_drive_service)):
|
|
result = client.share_document("doc123", "user@example.com", role="writer")
|
|
|
|
assert result["success"] is True
|
|
|
|
def test_get_document_success(self, tmp_path: Path) -> None:
|
|
"""Test successful document retrieval."""
|
|
creds_file = tmp_path / "credentials.json"
|
|
creds_file.write_text('{"test": "content"}')
|
|
|
|
config = GoogleDocsIntegrationConfig(
|
|
credentials_file=str(creds_file),
|
|
folder_id="folder123",
|
|
)
|
|
client = GoogleDocsClient(config)
|
|
|
|
# Mock the Docs service
|
|
mock_docs_service = MagicMock()
|
|
mock_documents = MagicMock()
|
|
mock_docs_service.documents.return_value = mock_documents
|
|
mock_get = MagicMock()
|
|
mock_documents.get.return_value = mock_get
|
|
mock_get.execute.return_value = {
|
|
"documentId": "doc123",
|
|
"title": "Test Document",
|
|
"body": {"content": []},
|
|
}
|
|
|
|
with patch.object(client, "_get_services", return_value=(mock_docs_service, None)):
|
|
result = client.get_document("doc123")
|
|
|
|
assert result["success"] is True
|
|
assert result["document_id"] == "doc123"
|
|
assert result["title"] == "Test Document"
|
|
|
|
|
|
class TestBuildGoogleDocsClientFromEnv:
|
|
"""Unit tests for build_google_docs_client_from_env function."""
|
|
|
|
@patch.dict("os.environ", {}, clear=True)
|
|
def test_returns_none_when_env_vars_missing(self) -> None:
|
|
"""Test returns None when environment variables are not set."""
|
|
result = build_google_docs_client_from_env()
|
|
assert result is None
|
|
|
|
@patch.dict(
|
|
"os.environ",
|
|
{
|
|
"GOOGLE_CREDENTIALS_FILE": "/path/to/creds.json",
|
|
"GOOGLE_DRIVE_FOLDER_ID": "folder123",
|
|
},
|
|
clear=True,
|
|
)
|
|
def test_returns_client_when_env_vars_set(self) -> None:
|
|
"""Test returns client when environment variables are set."""
|
|
result = build_google_docs_client_from_env()
|
|
assert result is not None
|
|
assert isinstance(result, GoogleDocsClient)
|
|
assert result.config.credentials_file == "/path/to/creds.json"
|
|
assert result.config.folder_id == "folder123"
|
|
|
|
|
|
class TestGoogleDocsIncidentReportTool:
|
|
"""Unit tests for create_google_docs_incident_report tool."""
|
|
|
|
@patch("integrations.google_docs.tools.GoogleDocsClient")
|
|
def test_create_report_success(
|
|
self,
|
|
mock_client_class: MagicMock,
|
|
) -> None:
|
|
"""Test successful incident report creation."""
|
|
mock_client = MagicMock()
|
|
mock_client.is_configured = True
|
|
mock_client.create_incident_report.return_value = {
|
|
"success": True,
|
|
"document_id": "doc123",
|
|
"document_url": "https://docs.google.com/document/d/doc123/edit",
|
|
"title": "Incident Report: Test",
|
|
}
|
|
mock_client_class.return_value = mock_client
|
|
|
|
result = create_google_docs_incident_report(
|
|
title="Incident Report: Test",
|
|
summary="Test incident summary",
|
|
root_cause="Test root cause",
|
|
severity="high",
|
|
credentials_file="/path/to/creds.json",
|
|
folder_id="folder123",
|
|
)
|
|
|
|
assert result["success"] is True
|
|
assert result["document_id"] == "doc123"
|
|
assert result["document_url"] == "https://docs.google.com/document/d/doc123/edit"
|
|
|
|
@patch("integrations.google_docs.tools.GoogleDocsClient")
|
|
def test_create_report_not_configured(
|
|
self,
|
|
mock_client_class: MagicMock,
|
|
) -> None:
|
|
"""Test report creation when client is not configured."""
|
|
mock_client = MagicMock()
|
|
mock_client.is_configured = False
|
|
mock_client_class.return_value = mock_client
|
|
|
|
result = create_google_docs_incident_report(
|
|
title="Incident Report: Test",
|
|
summary="Test incident summary",
|
|
root_cause="Test root cause",
|
|
severity="high",
|
|
credentials_file="/path/to/creds.json",
|
|
folder_id="folder123",
|
|
)
|
|
|
|
assert result["success"] is False
|
|
assert "not properly configured" in result["error"]
|
|
|
|
@patch("integrations.google_docs.tools.GoogleDocsClient")
|
|
def test_create_report_with_all_fields(
|
|
self,
|
|
mock_client_class: MagicMock,
|
|
) -> None:
|
|
"""Test report creation with all optional fields."""
|
|
mock_client = MagicMock()
|
|
mock_client.is_configured = True
|
|
mock_client.create_incident_report.return_value = {
|
|
"success": True,
|
|
"document_id": "doc123",
|
|
"document_url": "https://docs.google.com/document/d/doc123/edit",
|
|
"title": "Incident Report: Test",
|
|
}
|
|
mock_client_class.return_value = mock_client
|
|
|
|
evidence = [{"title": "Log entry", "description": "Error in logs"}]
|
|
timeline = [{"time": "10:00", "description": "Alert fired"}]
|
|
remediation_steps = ["Step 1", "Step 2"]
|
|
follow_up_actions = ["Action 1", "Action 2"]
|
|
|
|
result = create_google_docs_incident_report(
|
|
title="Incident Report: Test",
|
|
summary="Test incident summary",
|
|
root_cause="Test root cause",
|
|
severity="critical",
|
|
credentials_file="/path/to/creds.json",
|
|
folder_id="folder123",
|
|
evidence=evidence,
|
|
timeline=timeline,
|
|
remediation_steps=remediation_steps,
|
|
follow_up_actions=follow_up_actions,
|
|
share_with=["user@example.com"],
|
|
)
|
|
|
|
assert result["success"] is True
|
|
# Verify share_document was called
|
|
mock_client.share_document.assert_called_once_with(
|
|
"doc123", "user@example.com", role="writer"
|
|
)
|
|
|
|
@patch("integrations.google_docs.tools.GoogleDocsClient")
|
|
def test_create_report_api_error(
|
|
self,
|
|
mock_client_class: MagicMock,
|
|
) -> None:
|
|
"""Test report creation when API returns error."""
|
|
mock_client = MagicMock()
|
|
mock_client.is_configured = True
|
|
mock_client.create_incident_report.return_value = {
|
|
"success": False,
|
|
"error": "API Error",
|
|
}
|
|
mock_client_class.return_value = mock_client
|
|
|
|
result = create_google_docs_incident_report(
|
|
title="Incident Report: Test",
|
|
summary="Test incident summary",
|
|
root_cause="Test root cause",
|
|
severity="high",
|
|
credentials_file="/path/to/creds.json",
|
|
folder_id="folder123",
|
|
)
|
|
|
|
assert result["success"] is False
|
|
assert result["error"] == "API Error"
|
|
|
|
def test_tool_is_available_when_configured(self) -> None:
|
|
"""Test that is_available returns True when google_docs is configured."""
|
|
from integrations.google_docs.tools import _is_available
|
|
|
|
sources = {"google_docs": {"configured": True}}
|
|
assert _is_available(sources) is True
|
|
|
|
def test_tool_is_available_when_not_configured(self) -> None:
|
|
"""Test that is_available returns False when google_docs is not configured."""
|
|
from integrations.google_docs.tools import _is_available
|
|
|
|
sources = {}
|
|
assert _is_available(sources) is False
|
|
|
|
def test_tool_extract_params(self) -> None:
|
|
"""Test that extract_params returns correct parameters."""
|
|
from integrations.google_docs.tools import _extract_params
|
|
|
|
sources = {
|
|
"google_docs": {
|
|
"credentials_file": "/path/to/creds.json",
|
|
"folder_id": "folder123",
|
|
}
|
|
}
|
|
params = _extract_params(sources)
|
|
assert params["credentials_file"] == "/path/to/creds.json"
|
|
assert params["folder_id"] == "folder123"
|
|
|
|
@patch("integrations.google_docs.tools.GoogleDocsClient")
|
|
def test_create_report_with_custom_share_role(
|
|
self,
|
|
mock_client_class: MagicMock,
|
|
) -> None:
|
|
"""Test report creation with custom share_role parameter."""
|
|
mock_client = MagicMock()
|
|
mock_client.is_configured = True
|
|
mock_client.create_incident_report.return_value = {
|
|
"success": True,
|
|
"document_id": "doc123",
|
|
"document_url": "https://docs.google.com/document/d/doc123/edit",
|
|
"title": "Incident Report: Test",
|
|
}
|
|
mock_client_class.return_value = mock_client
|
|
|
|
result = create_google_docs_incident_report(
|
|
title="Incident Report: Test",
|
|
summary="Test incident summary",
|
|
root_cause="Test root cause",
|
|
severity="high",
|
|
credentials_file="/path/to/creds.json",
|
|
folder_id="folder123",
|
|
share_with=["user@example.com"],
|
|
share_role="reader",
|
|
)
|
|
|
|
assert result["success"] is True
|
|
# Verify share_document was called with reader role
|
|
mock_client.share_document.assert_called_once_with(
|
|
"doc123", "user@example.com", role="reader"
|
|
)
|
|
|
|
@patch("integrations.google_docs.tools.GoogleDocsClient")
|
|
def test_create_report_with_invalid_share_role_defaults_to_writer(
|
|
self,
|
|
mock_client_class: MagicMock,
|
|
) -> None:
|
|
"""Test that invalid share_role defaults to writer."""
|
|
mock_client = MagicMock()
|
|
mock_client.is_configured = True
|
|
mock_client.create_incident_report.return_value = {
|
|
"success": True,
|
|
"document_id": "doc123",
|
|
"document_url": "https://docs.google.com/document/d/doc123/edit",
|
|
"title": "Incident Report: Test",
|
|
}
|
|
mock_client_class.return_value = mock_client
|
|
|
|
result = create_google_docs_incident_report(
|
|
title="Incident Report: Test",
|
|
summary="Test incident summary",
|
|
root_cause="Test root cause",
|
|
severity="high",
|
|
credentials_file="/path/to/creds.json",
|
|
folder_id="folder123",
|
|
share_with=["user@example.com"],
|
|
share_role="invalid_role",
|
|
)
|
|
|
|
assert result["success"] is True
|
|
# Verify share_document was called with writer role (default)
|
|
mock_client.share_document.assert_called_once_with(
|
|
"doc123", "user@example.com", role="writer"
|
|
)
|
|
|
|
|
|
class TestGoogleDocsIntegrationConfigTimeout:
|
|
"""Unit tests for GoogleDocsIntegrationConfig timeout validation."""
|
|
|
|
def test_default_timeout(self) -> None:
|
|
"""Test default timeout is 30 seconds."""
|
|
config = GoogleDocsIntegrationConfig(
|
|
credentials_file="/path/to/creds.json",
|
|
folder_id="folder123",
|
|
)
|
|
assert config.timeout_seconds == 30
|
|
|
|
def test_custom_timeout(self) -> None:
|
|
"""Test custom timeout can be set."""
|
|
config = GoogleDocsIntegrationConfig(
|
|
credentials_file="/path/to/creds.json",
|
|
folder_id="folder123",
|
|
timeout_seconds=60,
|
|
)
|
|
assert config.timeout_seconds == 60
|
|
|
|
def test_timeout_minimum_bound(self) -> None:
|
|
"""Test timeout has minimum of 5 seconds."""
|
|
config = GoogleDocsIntegrationConfig(
|
|
credentials_file="/path/to/creds.json",
|
|
folder_id="folder123",
|
|
timeout_seconds=1, # Should be clamped to 5
|
|
)
|
|
assert config.timeout_seconds == 5
|
|
|
|
def test_timeout_maximum_bound(self) -> None:
|
|
"""Test timeout has maximum of 300 seconds."""
|
|
config = GoogleDocsIntegrationConfig(
|
|
credentials_file="/path/to/creds.json",
|
|
folder_id="folder123",
|
|
timeout_seconds=600, # Should be clamped to 300
|
|
)
|
|
assert config.timeout_seconds == 300
|
|
|
|
def test_timeout_invalid_value_defaults_to_30(self) -> None:
|
|
"""Test invalid timeout value defaults to 30."""
|
|
config = GoogleDocsIntegrationConfig(
|
|
credentials_file="/path/to/creds.json",
|
|
folder_id="folder123",
|
|
timeout_seconds="invalid", # type: ignore[arg-type]
|
|
)
|
|
assert config.timeout_seconds == 30
|
|
|
|
|
|
class TestGoogleDocsClientIncidentReport:
|
|
"""Unit tests for create_incident_report method."""
|
|
|
|
@patch.object(GoogleDocsClient, "_validate_folder_exists")
|
|
@patch.object(GoogleDocsClient, "create_document")
|
|
@patch.object(GoogleDocsClient, "insert_content")
|
|
def test_create_incident_report_with_all_params(
|
|
self,
|
|
mock_insert: MagicMock,
|
|
mock_create: MagicMock,
|
|
mock_validate: MagicMock,
|
|
tmp_path: Path,
|
|
) -> None:
|
|
"""Test create_incident_report with all parameters."""
|
|
creds_file = tmp_path / "credentials.json"
|
|
creds_file.write_text('{"test": "content"}')
|
|
|
|
mock_validate.return_value = {"success": True, "folder_name": "Test Folder"}
|
|
mock_create.return_value = {
|
|
"success": True,
|
|
"document_id": "doc123",
|
|
"document_url": "https://docs.google.com/document/d/doc123/edit",
|
|
}
|
|
mock_insert.return_value = {"success": True}
|
|
|
|
config = GoogleDocsIntegrationConfig(
|
|
credentials_file=str(creds_file),
|
|
folder_id="folder123",
|
|
)
|
|
client = GoogleDocsClient(config)
|
|
|
|
evidence = [
|
|
{"title": "Log Error", "description": "Database connection failed"},
|
|
{"title": "Metric Spike", "description": "CPU usage at 100%"},
|
|
]
|
|
timeline = [
|
|
{"time": "10:00 UTC", "description": "Alert triggered"},
|
|
{"time": "10:15 UTC", "description": "Investigation started"},
|
|
{"time": "11:30 UTC", "description": "Issue resolved"},
|
|
]
|
|
remediation_steps = ["Restarted database", "Scaled up instances"]
|
|
follow_up_actions = ["Add monitoring", "Review capacity planning"]
|
|
|
|
result = client.create_incident_report(
|
|
title="Incident: Database Outage",
|
|
summary="Database connection pool exhausted causing service degradation",
|
|
root_cause="Connection leak in application code",
|
|
evidence=evidence,
|
|
timeline=timeline,
|
|
severity="critical",
|
|
remediation_steps=remediation_steps,
|
|
follow_up_actions=follow_up_actions,
|
|
)
|
|
|
|
assert result["success"] is True
|
|
assert result["document_id"] == "doc123"
|
|
mock_validate.assert_called_once()
|
|
mock_create.assert_called_once_with("Incident: Database Outage")
|
|
mock_insert.assert_called_once()
|
|
|
|
@patch.object(GoogleDocsClient, "_validate_folder_exists")
|
|
def test_create_incident_report_folder_not_found(
|
|
self,
|
|
mock_validate: MagicMock,
|
|
tmp_path: Path,
|
|
) -> None:
|
|
"""Test create_incident_report when folder doesn't exist."""
|
|
creds_file = tmp_path / "credentials.json"
|
|
creds_file.write_text('{"test": "content"}')
|
|
|
|
mock_validate.return_value = {
|
|
"success": False,
|
|
"error": "Folder not found",
|
|
}
|
|
|
|
config = GoogleDocsIntegrationConfig(
|
|
credentials_file=str(creds_file),
|
|
folder_id="invalid_folder",
|
|
)
|
|
client = GoogleDocsClient(config)
|
|
|
|
result = client.create_incident_report(
|
|
title="Test Report",
|
|
summary="Test summary",
|
|
root_cause="Test cause",
|
|
evidence=[],
|
|
timeline=[],
|
|
severity="high",
|
|
)
|
|
|
|
assert result["success"] is False
|
|
assert "Folder not found" in result["error"]
|
|
|
|
|
|
class TestGoogleDocsClientErrorHandling:
|
|
"""Unit tests for Google API error handling."""
|
|
|
|
def test_handle_google_api_error_with_403(self) -> None:
|
|
"""Test handling of 403 permission error."""
|
|
from integrations.google_docs.client import _handle_google_api_error
|
|
|
|
class MockHttpError(Exception):
|
|
def __init__(self) -> None:
|
|
super().__init__()
|
|
self.resp = type("obj", (object,), {"status": 403})()
|
|
|
|
exc = MockHttpError()
|
|
result = _handle_google_api_error(exc, "creating document")
|
|
|
|
assert result["success"] is False
|
|
assert "Insufficient permissions" in result["error"]
|
|
|
|
def test_handle_google_api_error_with_404(self) -> None:
|
|
"""Test handling of 404 not found error."""
|
|
from integrations.google_docs.client import _handle_google_api_error
|
|
|
|
class MockHttpError(Exception):
|
|
def __init__(self) -> None:
|
|
super().__init__()
|
|
self.resp = type("obj", (object,), {"status": 404})()
|
|
|
|
exc = MockHttpError()
|
|
result = _handle_google_api_error(exc, "accessing folder")
|
|
|
|
assert result["success"] is False
|
|
assert "Resource not found" in result["error"]
|
|
|
|
def test_handle_google_api_error_with_429(self) -> None:
|
|
"""Test handling of 429 rate limit error."""
|
|
from integrations.google_docs.client import _handle_google_api_error
|
|
|
|
class MockHttpError(Exception):
|
|
def __init__(self) -> None:
|
|
super().__init__()
|
|
self.resp = type("obj", (object,), {"status": 429})()
|
|
|
|
exc = MockHttpError()
|
|
result = _handle_google_api_error(exc, "inserting content")
|
|
|
|
assert result["success"] is False
|
|
assert "Rate limit exceeded" in result["error"]
|
|
|
|
def test_handle_google_api_error_generic(self) -> None:
|
|
"""Test handling of generic error."""
|
|
from integrations.google_docs.client import _handle_google_api_error
|
|
|
|
exc = Exception("Something went wrong")
|
|
result = _handle_google_api_error(exc, "sharing document")
|
|
|
|
assert result["success"] is False
|
|
assert "Something went wrong" in result["error"]
|