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

This commit is contained in:
2026-03-17 19:23:33 -05:00
commit 395f0e2029
138 changed files with 41691 additions and 0 deletions

View File

@@ -0,0 +1,101 @@
import base64
import os
import sys
import pytest
def test_urlsafe_b64decode_already_handles_crlf():
"""Verify Python's urlsafe_b64decode ignores embedded CR/LF without manual stripping."""
original = b"Testdata"
b64 = base64.urlsafe_b64encode(original).decode()
assert base64.urlsafe_b64decode(b64 + "\n") == original
assert base64.urlsafe_b64decode(b64[:4] + "\r\n" + b64[4:]) == original
assert base64.urlsafe_b64decode(b64[:4] + "\r\r\n" + b64[4:]) == original
def test_os_open_without_o_binary_corrupts_on_windows(tmp_path):
"""On Windows, os.open without O_BINARY translates LF to CRLF in written bytes."""
payload = b"\x89PNG\r\n\x1a\n" + b"\x00" * 50
tmp = str(tmp_path / "test_no_binary.bin")
fd = os.open(tmp, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
try:
os.write(fd, payload)
finally:
os.close(fd)
with open(tmp, "rb") as f:
written = f.read()
if sys.platform == "win32":
assert written != payload, "Expected corruption without O_BINARY on Windows"
assert len(written) > len(payload)
else:
assert written == payload
def test_os_open_with_o_binary_preserves_bytes(tmp_path):
"""os.open with O_BINARY writes binary data correctly on all platforms."""
payload = b"\x89PNG\r\n\x1a\n" + b"\x00" * 50
tmp = str(tmp_path / "test_with_binary.bin")
flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC | getattr(os, "O_BINARY", 0)
fd = os.open(tmp, flags, 0o600)
try:
os.write(fd, payload)
finally:
os.close(fd)
with open(tmp, "rb") as f:
written = f.read()
assert written == payload
@pytest.fixture
def isolated_storage(tmp_path, monkeypatch):
"""Create an AttachmentStorage that writes to a temp directory."""
import core.attachment_storage as storage_module
monkeypatch.setattr(storage_module, "STORAGE_DIR", tmp_path)
return storage_module.AttachmentStorage()
def test_save_attachment_uses_binary_mode(isolated_storage):
"""Verify that AttachmentStorage.save_attachment writes files in binary mode."""
payload = b"\x89PNG\r\n\x1a\n" + b"\x00" * 100
b64_data = base64.urlsafe_b64encode(payload).decode()
result = isolated_storage.save_attachment(
b64_data, filename="test.png", mime_type="image/png"
)
with open(result.path, "rb") as f:
saved_bytes = f.read()
assert saved_bytes == payload, (
f"Binary corruption detected: wrote {len(payload)} bytes, "
f"read back {len(saved_bytes)} bytes"
)
@pytest.mark.parametrize(
"payload",
[
b"\x89PNG\r\n\x1a\n" + b"\xff" * 200, # PNG header
b"%PDF-1.7\n" + b"\x00" * 200, # PDF header
bytes(range(256)) * 4, # All byte values
],
)
def test_save_attachment_preserves_various_binary_formats(isolated_storage, payload):
"""Ensure binary integrity for payloads containing LF/CR bytes."""
b64_data = base64.urlsafe_b64encode(payload).decode()
result = isolated_storage.save_attachment(b64_data, filename="test.bin")
with open(result.path, "rb") as f:
saved_bytes = f.read()
assert saved_bytes == payload

View File

@@ -0,0 +1,288 @@
import base64
import os
import sys
from unittest.mock import Mock
import pytest
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../..")))
from core.utils import UserInputError
from gmail.gmail_tools import draft_gmail_message
def _unwrap(tool):
"""Unwrap FunctionTool + decorators to the original async function."""
fn = tool.fn if hasattr(tool, "fn") else tool
while hasattr(fn, "__wrapped__"):
fn = fn.__wrapped__
return fn
def _thread_response(*message_ids):
return {
"messages": [
{
"payload": {
"headers": [{"name": "Message-ID", "value": message_id}],
}
}
for message_id in message_ids
]
}
@pytest.mark.asyncio
async def test_draft_gmail_message_reports_actual_attachment_count(
tmp_path, monkeypatch
):
monkeypatch.setenv("ALLOWED_FILE_DIRS", str(tmp_path))
attachment_path = tmp_path / "sample.txt"
attachment_path.write_text("hello attachment", encoding="utf-8")
mock_service = Mock()
mock_service.users().drafts().create().execute.return_value = {"id": "draft123"}
result = await _unwrap(draft_gmail_message)(
service=mock_service,
user_google_email="user@example.com",
to="recipient@example.com",
subject="Attachment test",
body="Please see attached.",
attachments=[{"path": str(attachment_path)}],
include_signature=False,
)
assert "Draft created with 1 attachment(s)! Draft ID: draft123" in result
create_kwargs = (
mock_service.users.return_value.drafts.return_value.create.call_args.kwargs
)
raw_message = create_kwargs["body"]["message"]["raw"]
raw_bytes = base64.urlsafe_b64decode(raw_message)
assert b"Content-Disposition: attachment;" in raw_bytes
assert b"sample.txt" in raw_bytes
@pytest.mark.asyncio
async def test_draft_gmail_message_raises_when_no_attachments_are_added(
tmp_path, monkeypatch
):
monkeypatch.setenv("ALLOWED_FILE_DIRS", str(tmp_path))
missing_path = tmp_path / "missing.txt"
mock_service = Mock()
mock_service.users().drafts().create().execute.return_value = {"id": "draft123"}
with pytest.raises(UserInputError, match="No valid attachments were added"):
await _unwrap(draft_gmail_message)(
service=mock_service,
user_google_email="user@example.com",
to="recipient@example.com",
subject="Attachment test",
body="Please see attached.",
attachments=[{"path": str(missing_path)}],
include_signature=False,
)
@pytest.mark.asyncio
async def test_draft_gmail_message_appends_gmail_signature_html():
mock_service = Mock()
mock_service.users().drafts().create().execute.return_value = {"id": "draft_sig"}
mock_service.users().settings().sendAs().list().execute.return_value = {
"sendAs": [
{
"sendAsEmail": "user@example.com",
"isPrimary": True,
"signature": "<div>Best,<br>Alice</div>",
}
]
}
result = await _unwrap(draft_gmail_message)(
service=mock_service,
user_google_email="user@example.com",
to="recipient@example.com",
subject="Signature test",
body="<p>Hello</p>",
body_format="html",
include_signature=True,
)
assert "Draft created! Draft ID: draft_sig" in result
create_kwargs = (
mock_service.users.return_value.drafts.return_value.create.call_args.kwargs
)
raw_message = create_kwargs["body"]["message"]["raw"]
raw_text = base64.urlsafe_b64decode(raw_message).decode("utf-8", errors="ignore")
assert "<p>Hello</p>" in raw_text
assert "Best,<br>Alice" in raw_text
@pytest.mark.asyncio
async def test_draft_gmail_message_autofills_reply_headers_from_thread():
mock_service = Mock()
mock_service.users().drafts().create().execute.return_value = {"id": "draft_reply"}
mock_service.users().threads().get().execute.return_value = _thread_response(
"<msg1@example.com>",
"<msg2@example.com>",
"<msg3@example.com>",
)
result = await _unwrap(draft_gmail_message)(
service=mock_service,
user_google_email="user@example.com",
to="recipient@example.com",
subject="Meeting tomorrow",
body="Thanks for the update.",
thread_id="thread123",
include_signature=False,
)
# Verify threads().get() was called with correct parameters
thread_get_kwargs = (
mock_service.users.return_value.threads.return_value.get.call_args.kwargs
)
assert thread_get_kwargs["userId"] == "me"
assert thread_get_kwargs["id"] == "thread123"
assert thread_get_kwargs["format"] == "metadata"
assert "Message-ID" in thread_get_kwargs["metadataHeaders"]
assert "Draft created! Draft ID: draft_reply" in result
create_kwargs = (
mock_service.users.return_value.drafts.return_value.create.call_args.kwargs
)
raw_message = create_kwargs["body"]["message"]["raw"]
raw_text = base64.urlsafe_b64decode(raw_message).decode("utf-8", errors="ignore")
assert "In-Reply-To: <msg3@example.com>" in raw_text
assert (
"References: <msg1@example.com> <msg2@example.com> <msg3@example.com>"
in raw_text
)
assert create_kwargs["body"]["message"]["threadId"] == "thread123"
@pytest.mark.asyncio
async def test_draft_gmail_message_uses_explicit_in_reply_to_when_filling_references():
mock_service = Mock()
mock_service.users().drafts().create().execute.return_value = {"id": "draft_reply"}
mock_service.users().threads().get().execute.return_value = _thread_response(
"<msg1@example.com>",
"<msg2@example.com>",
"<msg3@example.com>",
)
await _unwrap(draft_gmail_message)(
service=mock_service,
user_google_email="user@example.com",
to="recipient@example.com",
subject="Meeting tomorrow",
body="Replying to an earlier message.",
thread_id="thread123",
in_reply_to="<msg2@example.com>",
include_signature=False,
)
create_kwargs = (
mock_service.users.return_value.drafts.return_value.create.call_args.kwargs
)
raw_message = create_kwargs["body"]["message"]["raw"]
raw_text = base64.urlsafe_b64decode(raw_message).decode("utf-8", errors="ignore")
assert "In-Reply-To: <msg2@example.com>" in raw_text
assert "References: <msg1@example.com> <msg2@example.com>" in raw_text
assert "<msg3@example.com>" not in raw_text
@pytest.mark.asyncio
async def test_draft_gmail_message_uses_explicit_references_when_filling_in_reply_to():
mock_service = Mock()
mock_service.users().drafts().create().execute.return_value = {"id": "draft_reply"}
mock_service.users().threads().get().execute.return_value = _thread_response(
"<msg1@example.com>",
"<msg2@example.com>",
"<msg3@example.com>",
)
await _unwrap(draft_gmail_message)(
service=mock_service,
user_google_email="user@example.com",
to="recipient@example.com",
subject="Meeting tomorrow",
body="Replying to an earlier message.",
thread_id="thread123",
references="<msg1@example.com> <msg2@example.com>",
include_signature=False,
)
create_kwargs = (
mock_service.users.return_value.drafts.return_value.create.call_args.kwargs
)
raw_message = create_kwargs["body"]["message"]["raw"]
raw_text = base64.urlsafe_b64decode(raw_message).decode("utf-8", errors="ignore")
assert "In-Reply-To: <msg2@example.com>" in raw_text
assert "References: <msg1@example.com> <msg2@example.com>" in raw_text
assert "<msg3@example.com>" not in raw_text
@pytest.mark.asyncio
async def test_draft_gmail_message_gracefully_degrades_when_thread_fetch_fails():
mock_service = Mock()
mock_service.users().drafts().create().execute.return_value = {"id": "draft_reply"}
mock_service.users().threads().get().execute.side_effect = RuntimeError("boom")
result = await _unwrap(draft_gmail_message)(
service=mock_service,
user_google_email="user@example.com",
to="recipient@example.com",
subject="Meeting tomorrow",
body="Thanks for the update.",
thread_id="thread123",
include_signature=False,
)
assert "Draft created! Draft ID: draft_reply" in result
create_kwargs = (
mock_service.users.return_value.drafts.return_value.create.call_args.kwargs
)
raw_message = create_kwargs["body"]["message"]["raw"]
raw_text = base64.urlsafe_b64decode(raw_message).decode("utf-8", errors="ignore")
assert "In-Reply-To:" not in raw_text
assert "References:" not in raw_text
@pytest.mark.asyncio
async def test_draft_gmail_message_gracefully_degrades_when_thread_has_no_messages():
mock_service = Mock()
mock_service.users().drafts().create().execute.return_value = {"id": "draft_reply"}
mock_service.users().threads().get().execute.return_value = {"messages": []}
result = await _unwrap(draft_gmail_message)(
service=mock_service,
user_google_email="user@example.com",
to="recipient@example.com",
subject="Meeting tomorrow",
body="Thanks for the update.",
thread_id="thread123",
include_signature=False,
)
assert "Draft created! Draft ID: draft_reply" in result
create_kwargs = (
mock_service.users.return_value.drafts.return_value.create.call_args.kwargs
)
raw_message = create_kwargs["body"]["message"]["raw"]
raw_text = base64.urlsafe_b64decode(raw_message).decode("utf-8", errors="ignore")
assert "In-Reply-To:" not in raw_text
assert "References:" not in raw_text