chore: import upstream snapshot with attribution
CodeQL / Analyze (csharp) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
dotnet-build-and-test / dotnet-test-functions (push) Has been cancelled
dotnet-build-and-test / paths-filter (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Debug, windows-latest, net9.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net8.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-foundry-hosted-it (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test-check (push) Has been cancelled
dotnet-build-and-test / Integration Test Report (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:39:25 +08:00
commit db620d33df
5151 changed files with 925932 additions and 0 deletions
@@ -0,0 +1,84 @@
# Copyright (c) Microsoft. All rights reserved.
"""Tests for the ChatKit integration sample attachment store."""
import importlib.util
import json
from io import BytesIO
from pathlib import Path
from types import ModuleType
import agent_framework
import pytest
_ATTACHMENT_STORE_PATH = (
Path(__file__).parents[3] / "samples" / "05-end-to-end" / "chatkit-integration" / "attachment_store.py"
)
def _load_attachment_store_module() -> ModuleType:
spec = importlib.util.spec_from_file_location("chatkit_attachment_store", _ATTACHMENT_STORE_PATH)
assert spec is not None
assert spec.loader is not None
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
attachment_store_module = _load_attachment_store_module()
@pytest.fixture
def chatkit_app_module(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> ModuleType:
sample_dir = _ATTACHMENT_STORE_PATH.parent
monkeypatch.chdir(tmp_path)
monkeypatch.setenv("FOUNDRY_MODEL", "test-model")
monkeypatch.setenv("FOUNDRY_PROJECT_ENDPOINT", "https://example.com")
monkeypatch.setattr(agent_framework, "FunctionResultContent", object, raising=False)
monkeypatch.syspath_prepend(str(sample_dir))
spec = importlib.util.spec_from_file_location("chatkit_integration_app", sample_dir / "app.py")
assert spec is not None
assert spec.loader is not None
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def test_get_file_path_returns_direct_child(tmp_path: Path) -> None:
store = attachment_store_module.FileBasedAttachmentStore(uploads_dir=str(tmp_path))
assert store.get_file_path("attachment-123") == tmp_path / "attachment-123"
@pytest.mark.parametrize(
"attachment_id",
[
"../outside",
"nested/attachment-123",
"nested/../attachment-123",
r"nested\attachment-123",
r"nested\..\attachment-123",
"/tmp/attachment-123",
"",
".",
"..",
],
)
def test_get_file_path_rejects_non_filename_ids(tmp_path: Path, attachment_id: str) -> None:
store = attachment_store_module.FileBasedAttachmentStore(uploads_dir=str(tmp_path))
with pytest.raises(ValueError, match="Invalid attachment ID"):
store.get_file_path(attachment_id)
async def test_attachment_routes_return_bad_request_for_invalid_id(chatkit_app_module: ModuleType) -> None:
upload = chatkit_app_module.UploadFile(file=BytesIO(b"contents"), filename="attachment.txt")
upload_response = await chatkit_app_module.upload_file(".", upload)
preview_response = await chatkit_app_module.preview_image(".")
assert upload_response.status_code == 400
assert json.loads(upload_response.body) == {"error": "Invalid attachment ID."}
assert preview_response.status_code == 400
assert json.loads(preview_response.body) == {"error": "Invalid attachment ID."}
@@ -0,0 +1,594 @@
# Copyright (c) Microsoft. All rights reserved.
import copy
import os
from collections.abc import Awaitable, Callable
from typing import Any
import pytest
from pytest import MonkeyPatch, mark, param
from samples.getting_started.agents.azure_ai.azure_ai_basic import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as azure_ai_basic,
)
from samples.getting_started.agents.azure_ai.azure_ai_with_code_interpreter import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as azure_ai_with_code_interpreter,
)
from samples.getting_started.agents.azure_ai.azure_ai_with_existing_agent import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as azure_ai_with_existing_agent,
)
from samples.getting_started.agents.azure_ai.azure_ai_with_explicit_settings import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as azure_ai_with_explicit_settings,
)
from samples.getting_started.agents.azure_ai.azure_ai_with_function_tools import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
mixed_tools_example as azure_ai_with_function_tools_mixed,
)
from samples.getting_started.agents.azure_ai.azure_ai_with_function_tools import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
tools_on_agent_level as azure_ai_with_function_tools_agent,
)
from samples.getting_started.agents.azure_ai.azure_ai_with_function_tools import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
tools_on_run_level as azure_ai_with_function_tools_run,
)
from samples.getting_started.agents.azure_ai.azure_ai_with_local_mcp import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as azure_ai_with_local_mcp,
)
from samples.getting_started.agents.azure_ai.azure_ai_with_thread import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as azure_ai_with_thread,
)
from samples.getting_started.agents.azure_openai.azure_assistants_basic import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as azure_assistants_basic,
)
from samples.getting_started.agents.azure_openai.azure_assistants_with_code_interpreter import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as azure_assistants_with_code_interpreter,
)
from samples.getting_started.agents.azure_openai.azure_assistants_with_existing_assistant import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as azure_assistants_with_existing_assistant,
)
from samples.getting_started.agents.azure_openai.azure_assistants_with_explicit_settings import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as azure_assistants_with_explicit_settings,
)
from samples.getting_started.agents.azure_openai.azure_assistants_with_function_tools import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as azure_assistants_with_function_tools,
)
from samples.getting_started.agents.azure_openai.azure_assistants_with_thread import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as azure_assistants_with_thread,
)
from samples.getting_started.agents.azure_openai.azure_chat_client_basic import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as azure_chat_client_basic,
)
from samples.getting_started.agents.azure_openai.azure_chat_client_with_explicit_settings import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as azure_chat_client_with_explicit_settings,
)
from samples.getting_started.agents.azure_openai.azure_chat_client_with_function_tools import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as azure_chat_client_with_function_tools,
)
from samples.getting_started.agents.azure_openai.azure_chat_client_with_thread import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as azure_chat_client_with_thread,
)
from samples.getting_started.agents.azure_openai.azure_responses_client_basic import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as azure_responses_client_basic,
)
from samples.getting_started.agents.azure_openai.azure_responses_client_with_code_interpreter import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as azure_responses_client_with_code_interpreter,
)
from samples.getting_started.agents.azure_openai.azure_responses_client_with_explicit_settings import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as azure_responses_client_with_explicit_settings,
)
from samples.getting_started.agents.azure_openai.azure_responses_client_with_function_tools import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as azure_responses_client_with_function_tools,
)
from samples.getting_started.agents.azure_openai.azure_responses_client_with_thread import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as azure_responses_client_with_thread,
)
from samples.getting_started.agents.openai.openai_assistants_basic import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as openai_assistants_basic,
)
from samples.getting_started.agents.openai.openai_assistants_with_code_interpreter import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as openai_assistants_with_code_interpreter,
)
from samples.getting_started.agents.openai.openai_assistants_with_existing_assistant import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as openai_assistants_with_existing_assistant,
)
from samples.getting_started.agents.openai.openai_assistants_with_explicit_settings import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as openai_assistants_with_explicit_settings,
)
from samples.getting_started.agents.openai.openai_assistants_with_file_search import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as openai_assistants_with_file_search,
)
from samples.getting_started.agents.openai.openai_assistants_with_function_tools import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as openai_assistants_with_function_tools,
)
from samples.getting_started.agents.openai.openai_assistants_with_thread import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as openai_assistants_with_thread,
)
from samples.getting_started.agents.openai.openai_chat_client_basic import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as openai_chat_client_basic,
)
from samples.getting_started.agents.openai.openai_chat_client_with_explicit_settings import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as openai_chat_client_with_explicit_settings,
)
from samples.getting_started.agents.openai.openai_chat_client_with_function_tools import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as openai_chat_client_with_function_tools,
)
from samples.getting_started.agents.openai.openai_chat_client_with_local_mcp import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as openai_chat_client_with_local_mcp,
)
from samples.getting_started.agents.openai.openai_chat_client_with_thread import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as openai_chat_client_with_thread,
)
from samples.getting_started.agents.openai.openai_chat_client_with_web_search import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as openai_chat_client_with_web_search,
)
from samples.getting_started.agents.openai.openai_responses_client_basic import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as openai_responses_client_basic,
)
from samples.getting_started.agents.openai.openai_responses_client_reasoning import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as openai_responses_client_reasoning,
)
from samples.getting_started.agents.openai.openai_responses_client_with_code_interpreter import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as openai_responses_client_with_code_interpreter,
)
from samples.getting_started.agents.openai.openai_responses_client_with_explicit_settings import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as openai_responses_client_with_explicit_settings,
)
from samples.getting_started.agents.openai.openai_responses_client_with_file_search import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as openai_responses_client_with_file_search,
)
from samples.getting_started.agents.openai.openai_responses_client_with_function_tools import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as openai_responses_client_with_function_tools,
)
from samples.getting_started.agents.openai.openai_responses_client_with_local_mcp import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as openai_responses_client_with_local_mcp,
)
from samples.getting_started.agents.openai.openai_responses_client_with_thread import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as openai_responses_client_with_thread,
)
from samples.getting_started.agents.openai.openai_responses_client_with_web_search import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as openai_responses_client_with_web_search,
)
# Environment variable for controlling sample tests
RUN_SAMPLES_TESTS = "RUN_SAMPLES_TESTS"
# All agent samples across providers
agent_samples = [
# Azure Assistants Agent samples
param(
azure_assistants_basic,
[], # Non-interactive sample
id="azure_assistants_basic",
marks=[
pytest.mark.azure,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
azure_assistants_with_code_interpreter,
[], # Non-interactive sample
id="azure_assistants_with_code_interpreter",
marks=[
pytest.mark.azure,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
azure_assistants_with_function_tools,
[], # Non-interactive sample
id="azure_assistants_with_function_tools",
marks=[
pytest.mark.azure,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
azure_assistants_with_existing_assistant,
[], # Non-interactive sample
id="azure_assistants_with_existing_assistant",
marks=[
pytest.mark.azure,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
azure_assistants_with_explicit_settings,
[], # Non-interactive sample
id="azure_assistants_with_explicit_settings",
marks=[
pytest.mark.azure,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
azure_assistants_with_thread,
[], # Non-interactive sample
id="azure_assistants_with_thread",
marks=[
pytest.mark.azure,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
# Azure Chat Client Agent samples
param(
azure_chat_client_basic,
[], # Non-interactive sample
id="azure_chat_client_basic",
marks=[
pytest.mark.azure,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
azure_chat_client_with_explicit_settings,
[], # Non-interactive sample
id="azure_chat_client_with_explicit_settings",
marks=[
pytest.mark.azure,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
azure_chat_client_with_function_tools,
[], # Non-interactive sample
id="azure_chat_client_with_function_tools",
marks=[
pytest.mark.azure,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
azure_chat_client_with_thread,
[], # Non-interactive sample
id="azure_chat_client_with_thread",
marks=[
pytest.mark.azure,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
# Azure Responses Client Agent samples
param(
azure_responses_client_basic,
[], # Non-interactive sample
id="azure_responses_client_basic",
marks=[
pytest.mark.azure,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
azure_responses_client_with_code_interpreter,
[], # Non-interactive sample
id="azure_responses_client_with_code_interpreter",
marks=[
pytest.mark.azure,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
azure_responses_client_with_explicit_settings,
[], # Non-interactive sample
id="azure_responses_client_with_explicit_settings",
marks=[
pytest.mark.azure,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
azure_responses_client_with_function_tools,
[], # Non-interactive sample
id="azure_responses_client_with_function_tools",
marks=[
pytest.mark.azure,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
azure_responses_client_with_thread,
[], # Non-interactive sample
id="azure_responses_client_with_thread",
marks=[
pytest.mark.azure,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
# Azure AI Agent samples
param(
azure_ai_basic,
[], # Non-interactive sample
id="azure_ai_basic",
marks=[
pytest.mark.azure_ai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
azure_ai_with_code_interpreter,
[], # Non-interactive sample
id="azure_ai_with_code_interpreter",
marks=[
pytest.mark.azure_ai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
azure_ai_with_existing_agent,
[], # Non-interactive sample
id="azure_ai_with_existing_agent",
marks=[
pytest.mark.azure_ai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
azure_ai_with_explicit_settings,
[], # Non-interactive sample
id="azure_ai_with_explicit_settings",
marks=[
pytest.mark.azure_ai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
azure_ai_with_function_tools_agent,
[], # Non-interactive sample
id="azure_ai_with_function_tools",
marks=[
pytest.mark.azure_ai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
azure_ai_with_function_tools_run,
[], # Non-interactive sample
id="azure_ai_with_function_tools",
marks=[
pytest.mark.azure_ai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
azure_ai_with_function_tools_mixed,
[], # Non-interactive sample
id="azure_ai_with_function_tools",
marks=[
pytest.mark.azure_ai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
azure_ai_with_thread,
[], # Non-interactive sample
id="azure_ai_with_thread",
marks=[
pytest.mark.azure_ai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
azure_ai_with_local_mcp,
[], # Non-interactive sample
id="azure_ai_with_local_mcp",
marks=[
pytest.mark.azure_ai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
# OpenAI Assistants Agent samples
param(
openai_assistants_basic,
[], # Non-interactive sample
id="openai_assistants_basic",
marks=[
pytest.mark.openai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
openai_assistants_with_code_interpreter,
[], # Non-interactive sample
id="openai_assistants_with_code_interpreter",
marks=[
pytest.mark.openai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
openai_assistants_with_existing_assistant,
[], # Non-interactive sample
id="openai_assistants_with_existing_assistant",
marks=[
pytest.mark.openai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
openai_assistants_with_explicit_settings,
[], # Non-interactive sample
id="openai_assistants_with_explicit_settings",
marks=[
pytest.mark.openai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
openai_assistants_with_file_search,
[], # Non-interactive sample
id="openai_assistants_with_file_search",
marks=[
pytest.mark.openai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
pytest.mark.skip(reason="OpenAI file search functionality is currently broken - tracked in GitHub issue"),
],
),
param(
openai_assistants_with_function_tools,
[], # Non-interactive sample
id="openai_assistants_with_function_tools",
marks=[
pytest.mark.openai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
openai_assistants_with_thread,
[], # Non-interactive sample
id="openai_assistants_with_thread",
marks=[
pytest.mark.openai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
# OpenAI Chat Client Agent samples
param(
openai_chat_client_basic,
[], # Non-interactive sample
id="openai_chat_client_basic",
marks=[
pytest.mark.openai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
openai_chat_client_with_explicit_settings,
[], # Non-interactive sample
id="openai_chat_client_with_explicit_settings",
marks=[
pytest.mark.openai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
openai_chat_client_with_function_tools,
[], # Non-interactive sample
id="openai_chat_client_with_function_tools",
marks=[
pytest.mark.openai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
openai_chat_client_with_local_mcp,
[], # Non-interactive sample
id="openai_chat_client_with_local_mcp",
marks=[
pytest.mark.openai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
openai_chat_client_with_thread,
[], # Non-interactive sample
id="openai_chat_client_with_thread",
marks=[
pytest.mark.openai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
openai_chat_client_with_web_search,
[], # Non-interactive sample
id="openai_chat_client_with_web_search",
marks=[
pytest.mark.openai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
# OpenAI Responses Client Agent samples
param(
openai_responses_client_basic,
[], # Non-interactive sample
id="openai_responses_client_basic",
marks=[
pytest.mark.openai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
openai_responses_client_reasoning,
[], # Non-interactive sample
id="openai_responses_client_reasoning",
marks=[
pytest.mark.openai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
openai_responses_client_with_code_interpreter,
[], # Non-interactive sample
id="openai_responses_client_with_code_interpreter",
marks=[
pytest.mark.openai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
openai_responses_client_with_explicit_settings,
[], # Non-interactive sample
id="openai_responses_client_with_explicit_settings",
marks=[
pytest.mark.openai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
openai_responses_client_with_file_search,
[], # Non-interactive sample
id="openai_responses_client_with_file_search",
marks=[
pytest.mark.openai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
pytest.mark.skip(reason="OpenAI file search functionality is currently broken - tracked in GitHub issue"),
],
),
param(
openai_responses_client_with_function_tools,
[], # Non-interactive sample
id="openai_responses_client_with_function_tools",
marks=[
pytest.mark.openai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
openai_responses_client_with_local_mcp,
[], # Non-interactive sample
id="openai_responses_client_with_local_mcp",
marks=[
pytest.mark.openai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
openai_responses_client_with_thread,
[], # Non-interactive sample
id="openai_responses_client_with_thread",
marks=[
pytest.mark.openai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
openai_responses_client_with_web_search,
[], # Non-interactive sample
id="openai_responses_client_with_web_search",
marks=[
pytest.mark.openai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
]
@pytest.mark.flaky
@mark.parametrize("sample, responses", agent_samples)
async def test_agent_samples(sample: Callable[..., Awaitable[Any]], responses: list[str], monkeypatch: MonkeyPatch):
"""Test agent samples with input mocking and retry logic."""
saved_responses = copy.deepcopy(responses)
def reset():
responses.clear()
responses.extend(saved_responses)
def mock_input(prompt: str = "") -> str:
return responses.pop(0) if responses else "exit"
monkeypatch.setattr("builtins.input", mock_input)
await sample()
@@ -0,0 +1,133 @@
# Copyright (c) Microsoft. All rights reserved.
import copy
import os
from collections.abc import Awaitable, Callable
from typing import Any
import pytest
from pytest import MonkeyPatch, mark, param
from samples.getting_started.client.azure_ai_chat_client import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as azure_ai_chat_client,
)
from samples.getting_started.client.azure_assistants_client import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as azure_assistants_client,
)
from samples.getting_started.client.azure_chat_client import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as azure_chat_client,
)
from samples.getting_started.client.azure_responses_client import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as azure_responses_client,
)
from samples.getting_started.client.chat_response_cancellation import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as chat_response_cancellation,
)
from samples.getting_started.client.openai_assistants_client import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as openai_assistants_client,
)
from samples.getting_started.client.openai_chat_client import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as openai_chat_client,
)
from samples.getting_started.client.openai_responses_client import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as openai_responses_client,
)
# Environment variable for controlling sample tests
RUN_SAMPLES_TESTS = "RUN_SAMPLES_TESTS"
# All chat client samples across providers
chat_client_samples = [
# Azure Chat Client samples
param(
azure_assistants_client,
[], # Non-interactive sample
id="azure_assistants_client",
marks=[
pytest.mark.azure,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
azure_chat_client,
[], # Non-interactive sample
id="azure_chat_client",
marks=[
pytest.mark.azure,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
azure_responses_client,
[], # Non-interactive sample
id="azure_responses_client",
marks=[
pytest.mark.azure,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
# Azure AI Chat Client samples
param(
azure_ai_chat_client,
[], # Non-interactive sample
id="azure_ai_chat_client",
marks=[
pytest.mark.azure_ai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
# OpenAI Chat Client samples
param(
openai_assistants_client,
[], # Non-interactive sample
id="openai_assistants_client",
marks=[
pytest.mark.openai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
openai_chat_client,
[], # Non-interactive sample
id="openai_chat_client",
marks=[
pytest.mark.openai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
openai_responses_client,
[], # Non-interactive sample
id="openai_responses_client",
marks=[
pytest.mark.openai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
# General Chat Client samples (no provider-specific environment variable)
param(
chat_response_cancellation,
[], # Non-interactive sample
id="chat_response_cancellation",
marks=pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
),
]
@mark.parametrize("sample, responses", chat_client_samples)
async def test_chat_client_samples(
sample: Callable[..., Awaitable[Any]],
responses: list[str],
monkeypatch: MonkeyPatch,
):
"""Test chat client samples with input mocking and retry logic."""
saved_responses = copy.deepcopy(responses)
def reset():
responses.clear()
responses.extend(saved_responses)
def mock_input(prompt: str = "") -> str:
return responses.pop(0) if responses else "exit"
monkeypatch.setattr("builtins.input", mock_input)
await sample()
@@ -0,0 +1,56 @@
# Copyright (c) Microsoft. All rights reserved.
import copy
import os
from collections.abc import Awaitable, Callable
from typing import Any
import pytest
from pytest import MonkeyPatch, mark, param
from samples.getting_started.threads.custom_chat_message_store_thread import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as threads_custom_store,
)
from samples.getting_started.threads.suspend_resume_thread import ( # pyrefly: ignore[missing-import] # ty: ignore[unresolved-import]
main as threads_suspend_resume,
)
# Environment variable for controlling sample tests
RUN_SAMPLES_TESTS = "RUN_SAMPLES_TESTS"
# All thread samples
thread_samples = [
param(
threads_custom_store,
[], # Non-interactive sample
id="threads_custom_store",
marks=[
pytest.mark.openai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
param(
threads_suspend_resume,
[], # Non-interactive sample
id="threads_suspend_resume",
marks=[
pytest.mark.openai,
pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."),
],
),
]
@mark.parametrize("sample, responses", thread_samples)
async def test_thread_samples(sample: Callable[..., Awaitable[Any]], responses: list[str], monkeypatch: MonkeyPatch):
"""Test thread samples with input mocking and retry logic."""
saved_responses = copy.deepcopy(responses)
def reset():
responses.clear()
responses.extend(saved_responses)
def mock_input(prompt: str = "") -> str:
return responses.pop(0) if responses else "exit"
monkeypatch.setattr("builtins.input", mock_input)
await sample()
@@ -0,0 +1,99 @@
# Copyright (c) Microsoft. All rights reserved.
"""Unit tests for resolve_toolbox_endpoint() in the foundry-hosted-agents response samples.
Covers both 04_foundry_toolbox/main.py and 06_files/main.py which share the same
implementation of resolve_toolbox_endpoint().
"""
import importlib.util
import sys
from pathlib import Path
from unittest.mock import MagicMock
import pytest
# ---------------------------------------------------------------------------
# Stub out packages unavailable in the unit-test environment so that importing
# the sample modules does not fail.
# ---------------------------------------------------------------------------
_MISSING_MODULES = (
"agent_framework_foundry_hosting",
"azure.ai.agentserver",
"azure.ai.agentserver.responses",
)
for _mod_name in _MISSING_MODULES:
sys.modules.setdefault(_mod_name, MagicMock())
# ---------------------------------------------------------------------------
# Load the two sample modules by file path to avoid needing them on sys.path.
# ---------------------------------------------------------------------------
_RESPONSES_DIR = (
Path(__file__).parent.parent.parent.parent / "samples" / "04-hosting" / "foundry-hosted-agents" / "responses"
)
def _load_sample(subdir: str, module_alias: str):
spec = importlib.util.spec_from_file_location(module_alias, _RESPONSES_DIR / subdir / "main.py")
assert spec is not None
mod = importlib.util.module_from_spec(spec)
assert spec.loader is not None
spec.loader.exec_module(mod)
return mod
_toolbox_mod = _load_sample("04_foundry_toolbox", "foundry_toolbox_main")
_files_mod = _load_sample("06_files", "files_main")
# ---------------------------------------------------------------------------
# Parameterise over both modules so the same test cases run for each.
# ---------------------------------------------------------------------------
@pytest.fixture(params=["04_foundry_toolbox", "06_files"])
def resolve_endpoint(request):
"""Return resolve_toolbox_endpoint from the requested sample module."""
mod = _toolbox_mod if request.param == "04_foundry_toolbox" else _files_mod
return mod.resolve_toolbox_endpoint
class TestResolveToolboxEndpoint:
def test_explicit_endpoint_returned_as_is(self, resolve_endpoint, monkeypatch: pytest.MonkeyPatch):
monkeypatch.setenv("TOOLBOX_ENDPOINT", "https://example.com/mcp")
monkeypatch.delenv("FOUNDRY_PROJECT_ENDPOINT", raising=False)
monkeypatch.delenv("TOOLBOX_NAME", raising=False)
assert resolve_endpoint() == "https://example.com/mcp"
def test_empty_string_raises_value_error(self, resolve_endpoint, monkeypatch: pytest.MonkeyPatch):
monkeypatch.setenv("TOOLBOX_ENDPOINT", "")
with pytest.raises(ValueError, match="TOOLBOX_ENDPOINT is set but empty"):
resolve_endpoint()
def test_fallback_constructs_url_from_project_vars(self, resolve_endpoint, monkeypatch: pytest.MonkeyPatch):
monkeypatch.delenv("TOOLBOX_ENDPOINT", raising=False)
monkeypatch.setenv("FOUNDRY_PROJECT_ENDPOINT", "https://project.azure.com/")
monkeypatch.setenv("TOOLBOX_NAME", "my-toolbox")
result = resolve_endpoint()
assert result == "https://project.azure.com/toolboxes/my-toolbox/mcp?api-version=v1"
def test_fallback_strips_trailing_slash_from_project_endpoint(
self, resolve_endpoint, monkeypatch: pytest.MonkeyPatch
):
monkeypatch.delenv("TOOLBOX_ENDPOINT", raising=False)
monkeypatch.setenv("FOUNDRY_PROJECT_ENDPOINT", "https://project.azure.com///")
monkeypatch.setenv("TOOLBOX_NAME", "my-toolbox")
result = resolve_endpoint()
assert result == "https://project.azure.com/toolboxes/my-toolbox/mcp?api-version=v1"
def test_neither_variable_group_set_raises_value_error(self, resolve_endpoint, monkeypatch: pytest.MonkeyPatch):
monkeypatch.delenv("TOOLBOX_ENDPOINT", raising=False)
monkeypatch.delenv("FOUNDRY_PROJECT_ENDPOINT", raising=False)
monkeypatch.delenv("TOOLBOX_NAME", raising=False)
with pytest.raises(ValueError, match="Either set TOOLBOX_ENDPOINT"):
resolve_endpoint()