-
Notifications
You must be signed in to change notification settings - Fork 42
Integration testing framework #211
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 36 commits
cfb8847
4d93a46
be2fb1c
0d5539a
bf4c006
ebe622d
45678e9
c2b84cb
2816e68
2214827
c90c8e4
36f5c3b
dfdd959
d4828fb
91b3c44
1eefe44
a06da9a
d36ec7a
c096048
5451ddf
c2cdd40
7d91ef9
87610a5
ddfdd0b
dd0a87d
6a4e8ef
7793790
3ebbd44
a6681bf
1a7264b
9ba4a43
fab4368
a111155
a2a1092
ae8a286
9a5a6a8
aad011a
be0032a
e3f4324
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| # Microsoft 365 Agents SDK for Python Integration Testing Framework | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| aioresponses |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| from .application_runner import ApplicationRunner | ||
| from .aiohttp import AiohttpEnvironment | ||
| from .client import ( | ||
| AgentClient, | ||
| ResponseClient, | ||
| ) | ||
| from .environment import Environment | ||
| from .integration import integration, IntegrationFixtures | ||
| from .sample import Sample | ||
|
|
||
|
|
||
| __all__ = [ | ||
| "AgentClient", | ||
| "ApplicationRunner", | ||
| "AiohttpEnvironment", | ||
| "ResponseClient", | ||
| "Environment", | ||
| "integration", | ||
| "IntegrationFixtures", | ||
| "Sample", | ||
| ] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| from .aiohttp_environment import AiohttpEnvironment | ||
| from .aiohttp_runner import AiohttpRunner | ||
|
|
||
| __all__ = ["AiohttpEnvironment", "AiohttpRunner"] |
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,58 @@ | ||||||||||
| from tkinter import E | ||||||||||
|
||||||||||
| from tkinter import E |
Copilot
AI
Nov 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Import of 'E' is not used.
| from tkinter import E |
Copilot
AI
Nov 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Import of 'run_app' is not used.
| from aiohttp.web import Request, Response, Application, run_app | |
| from aiohttp.web import Request, Response, Application |
Copilot
AI
Nov 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused import run_app. This import is not used in the file and should be removed.
| from aiohttp.web import Request, Response, Application, run_app | |
| from aiohttp.web import Request, Response, Application |
Copilot
AI
Nov 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method requires 3 positional arguments, whereas overridden Environment.create_runner requires 1.
Copilot
AI
Nov 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method requires 3 positional arguments, whereas overridden Environment.create_runner requires 1.
| def create_runner(self, host: str, port: int) -> ApplicationRunner: | |
| def create_runner(self, runner_config: dict) -> ApplicationRunner: | |
| host = runner_config.get("host", "127.0.0.1") | |
| port = runner_config.get("port", 8080) |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,115 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from typing import Optional | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from typing import Optional | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from typing import Optional |
Copilot
AI
Nov 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Duplicate import statement for typing.Optional. Remove the duplicate on line 2.
| from typing import Optional |
Copilot
AI
Nov 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re-raising the caught exception without modification is unnecessary. Either remove the try-except block entirely or add error handling logic. Simply re-raising defeats the purpose of catching the exception.
| try: | |
| assert isinstance(self._app, Application) | |
| self._runner = AppRunner(self._app) | |
| await self._runner.setup() | |
| self._site = TCPSite(self._runner, self._host, self._port) | |
| await self._site.start() | |
| # Wait for shutdown signal | |
| while not self._shutdown_event.is_set(): | |
| await asyncio.sleep(0.1) | |
| # Cleanup | |
| await self._site.stop() | |
| await self._runner.cleanup() | |
| except Exception as error: | |
| raise error | |
| assert isinstance(self._app, Application) | |
| self._runner = AppRunner(self._app) | |
| await self._runner.setup() | |
| self._site = TCPSite(self._runner, self._host, self._port) | |
| await self._site.start() | |
| # Wait for shutdown signal | |
| while not self._shutdown_event.is_set(): | |
| await asyncio.sleep(0.1) | |
| # Cleanup | |
| await self._site.stop() | |
| await self._runner.cleanup() |
Copilot
AI
Nov 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Error message references 'ResponseClient' but this is the AiohttpRunner class. The message should be 'AiohttpRunner is already running.' or 'Server is already running.'
| raise RuntimeError("ResponseClient is already running.") | |
| raise RuntimeError("AiohttpRunner is already running.") |
Copilot
AI
Nov 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Error message mentions 'ResponseClient' but this is the AiohttpRunner class. The message should say 'AiohttpRunner is already running.' or 'Server is already running.'
| raise RuntimeError("ResponseClient is already running.") | |
| raise RuntimeError("Server is already running.") |
Copilot
AI
Nov 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re-raising the exception without modification is unnecessary. Either remove the try-except block entirely or handle the exception meaningfully (e.g., logging, cleanup, or error transformation).
Copilot
AI
Nov 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Error message refers to 'ResponseClient' but this is the AiohttpRunner class. Change the message to 'AiohttpRunner is already running.' or 'Server is already running.'
Copilot
AI
Nov 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Error message references 'ResponseClient' but this is the AiohttpRunner class. The message should be 'AiohttpRunner is not running.' or 'Server is not running.'
Copilot
AI
Nov 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Error message refers to 'ResponseClient' but this is the AiohttpRunner class. The message should say 'AiohttpRunner is not running.' or 'Server is not running.'
Copilot
AI
Nov 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Error message references 'ResponseClient' but this is the AiohttpRunner class. The message should be 'AiohttpRunner is not running.' or 'Server is not running.'
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| import asyncio | ||
| from abc import ABC, abstractmethod | ||
| from typing import Any, Optional | ||
| from threading import Thread | ||
|
|
||
|
|
||
| class ApplicationRunner(ABC): | ||
| """Base class for application runners.""" | ||
|
|
||
| def __init__(self, app: Any): | ||
| self._app = app | ||
| self._thread: Optional[Thread] = None | ||
|
|
||
| @abstractmethod | ||
| async def _start_server(self) -> None: | ||
| raise NotImplementedError( | ||
| "Start server method must be implemented by subclasses" | ||
| ) | ||
|
|
||
| async def _stop_server(self) -> None: | ||
| pass | ||
|
|
||
| async def __aenter__(self) -> None: | ||
|
||
|
|
||
| if self._thread: | ||
| raise RuntimeError("Server is already running") | ||
|
|
||
| def target(): | ||
| asyncio.run(self._start_server()) | ||
|
|
||
| self._thread = Thread(target=target, daemon=True) | ||
| self._thread.start() | ||
|
|
||
| async def __aexit__(self, exc_type, exc_val, exc_tb) -> None: | ||
|
|
||
| if self._thread: | ||
| await self._stop_server() | ||
|
|
||
| self._thread.join() | ||
| self._thread = None | ||
| else: | ||
| raise RuntimeError("Server is not running") | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| from .agent_client import AgentClient | ||
| from .response_client import ResponseClient | ||
|
|
||
| __all__ = [ | ||
| "AgentClient", | ||
| "ResponseClient", | ||
| ] |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,136 @@ | ||||||
| import os | ||||||
|
||||||
| import os |
Copilot
AI
Nov 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The 'os' module is imported but not used anywhere in this file. Remove this unused import.
| import os |
Copilot
AI
Oct 29, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Import of 'asyncio' is not used.
| import asyncio |
Copilot
AI
Oct 29, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Variable timeout is not used.
Copilot
AI
Nov 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Variable timeout is not used.
Copilot
AI
Nov 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Variable timeout is not used.
Copilot
AI
Oct 29, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Variable timeout is not used.
Copilot
AI
Nov 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Variable timeout is not used.
Copilot
AI
Nov 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Variable timeout is not used.
Copilot
AI
Nov 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Variable timeout is not used.
Copilot
AI
Nov 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hardcoded fallback URL 'http://localhost' with a comment indicating this is a 'temporary fix'. Consider making this configurable or documenting why this default is necessary. If this is truly temporary, create a TODO or track it for proper resolution.
Copilot
AI
Nov 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Temporary fix comment indicates incomplete implementation. Consider either completing the proper implementation or creating a TODO/FIXME comment explaining what needs to be addressed.
| activity.service_url = activity.service_url or "http://localhost" # temporary fix | |
| activity.service_url = activity.service_url or "http://localhost" # TODO: Replace hardcoded 'http://localhost' with the appropriate service URL. This is a temporary workaround for missing service_url. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused import of 'E' from 'tkinter'. This appears to be a leftover import that should be removed as tkinter is not used in this file.