Skip to content

Commit c387897

Browse files
committed
add some tests; not working yet
1 parent f15629c commit c387897

6 files changed

Lines changed: 870 additions & 8 deletions

File tree

tests/sdk/test_async_agent.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
"""Comprehensive tests for async AsyncAgent class."""
2+
3+
from __future__ import annotations
4+
5+
from unittest.mock import AsyncMock
6+
7+
import pytest
8+
9+
from tests.sdk.conftest import MockAgentView
10+
from runloop_api_client.sdk import AsyncAgent
11+
12+
13+
class TestAsyncAgent:
14+
"""Tests for AsyncAgent class."""
15+
16+
def test_init(self, mock_async_client: AsyncMock) -> None:
17+
"""Test AsyncAgent initialization."""
18+
agent = AsyncAgent(mock_async_client, "agent_123")
19+
assert agent.id == "agent_123"
20+
21+
def test_repr(self, mock_async_client: AsyncMock) -> None:
22+
"""Test AsyncAgent string representation."""
23+
agent = AsyncAgent(mock_async_client, "agent_123")
24+
assert repr(agent) == "<AsyncAgent id='agent_123'>"
25+
26+
@pytest.mark.asyncio
27+
async def test_get_info(self, mock_async_client: AsyncMock, agent_view: MockAgentView) -> None:
28+
"""Test get_info method."""
29+
mock_async_client.agents.retrieve = AsyncMock(return_value=agent_view)
30+
31+
agent = AsyncAgent(mock_async_client, "agent_123")
32+
result = await agent.get_info(
33+
extra_headers={"X-Custom": "value"},
34+
extra_query={"param": "value"},
35+
extra_body={"key": "value"},
36+
timeout=30.0,
37+
)
38+
39+
assert result == agent_view
40+
mock_async_client.agents.retrieve.assert_called_once_with(
41+
"agent_123",
42+
extra_headers={"X-Custom": "value"},
43+
extra_query={"param": "value"},
44+
extra_body={"key": "value"},
45+
timeout=30.0,
46+
)

tests/sdk/test_async_ops.py

Lines changed: 116 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,18 @@
1616
MockScorerView,
1717
MockSnapshotView,
1818
MockBlueprintView,
19+
MockAgentView,
1920
create_mock_httpx_response,
2021
)
21-
from runloop_api_client.sdk import AsyncDevbox, AsyncScorer, AsyncSnapshot, AsyncBlueprint, AsyncStorageObject
22+
from runloop_api_client.sdk import AsyncAgent, AsyncDevbox, AsyncScorer, AsyncSnapshot, AsyncBlueprint, AsyncStorageObject
2223
from runloop_api_client.sdk.async_ import (
2324
AsyncDevboxOps,
2425
AsyncScorerOps,
2526
AsyncRunloopSDK,
2627
AsyncSnapshotOps,
2728
AsyncBlueprintOps,
2829
AsyncStorageObjectOps,
30+
AsyncAgentOps,
2931
)
3032
from runloop_api_client.lib.polling import PollingConfig
3133

@@ -721,6 +723,118 @@ async def async_iter():
721723
assert scorers[1].id == "scorer_002"
722724
mock_async_client.scenarios.scorers.list.assert_awaited_once()
723725

726+
727+
class TestAsyncAgentClient:
728+
"""Tests for AsyncAgentClient class."""
729+
730+
@pytest.mark.asyncio
731+
async def test_create(self, mock_async_client: AsyncMock, agent_view: MockAgentView) -> None:
732+
"""Test create method."""
733+
mock_async_client.agents.create = AsyncMock(return_value=agent_view)
734+
735+
client = AsyncAgentOps(mock_async_client)
736+
agent = await client.create(
737+
name="test-agent",
738+
metadata={"key": "value"},
739+
)
740+
741+
assert isinstance(agent, AsyncAgent)
742+
assert agent.id == "agent_123"
743+
mock_async_client.agents.create.assert_called_once()
744+
745+
def test_from_id(self, mock_async_client: AsyncMock) -> None:
746+
"""Test from_id method."""
747+
client = AsyncAgentOps(mock_async_client)
748+
agent = client.from_id("agent_123")
749+
750+
assert isinstance(agent, AsyncAgent)
751+
assert agent.id == "agent_123"
752+
753+
@pytest.mark.asyncio
754+
async def test_list(self, mock_async_client: AsyncMock) -> None:
755+
"""Test list method."""
756+
# Create three agent views with different data
757+
agent_view_1 = MockAgentView(
758+
id="agent_001",
759+
name="first-agent",
760+
create_time_ms=1234567890000,
761+
is_public=False,
762+
source=None,
763+
)
764+
agent_view_2 = MockAgentView(
765+
id="agent_002",
766+
name="second-agent",
767+
create_time_ms=1234567891000,
768+
is_public=True,
769+
source={"type": "git", "git": {"repository": "https://github.com/example/repo"}},
770+
)
771+
agent_view_3 = MockAgentView(
772+
id="agent_003",
773+
name="third-agent",
774+
create_time_ms=1234567892000,
775+
is_public=False,
776+
source={"type": "npm", "npm": {"package_name": "example-package"}},
777+
)
778+
779+
page = SimpleNamespace(agents=[agent_view_1, agent_view_2, agent_view_3])
780+
mock_async_client.agents.list = AsyncMock(return_value=page)
781+
782+
# Mock retrieve to return the corresponding agent_view when called
783+
async def mock_retrieve(agent_id, **kwargs):
784+
if agent_id == "agent_001":
785+
return agent_view_1
786+
elif agent_id == "agent_002":
787+
return agent_view_2
788+
elif agent_id == "agent_003":
789+
return agent_view_3
790+
return None
791+
792+
mock_async_client.agents.retrieve = AsyncMock(side_effect=mock_retrieve)
793+
794+
client = AsyncAgentOps(mock_async_client)
795+
agents = await client.list(
796+
limit=10,
797+
starting_after="agent_000",
798+
)
799+
800+
# Verify we got three agents
801+
assert len(agents) == 3
802+
assert all(isinstance(agent, AsyncAgent) for agent in agents)
803+
804+
# Verify the agent IDs
805+
assert agents[0].id == "agent_001"
806+
assert agents[1].id == "agent_002"
807+
assert agents[2].id == "agent_003"
808+
809+
# Test that get_info() retrieves the AgentView for the first agent
810+
info = await agents[0].get_info()
811+
assert info.id == "agent_001"
812+
assert info.name == "first-agent"
813+
assert info.create_time_ms == 1234567890000
814+
assert info.is_public is False
815+
assert info.source is None
816+
817+
# Test that get_info() retrieves the AgentView for the second agent
818+
info = await agents[1].get_info()
819+
assert info.id == "agent_002"
820+
assert info.name == "second-agent"
821+
assert info.create_time_ms == 1234567891000
822+
assert info.is_public is True
823+
assert info.source == {"type": "git", "git": {"repository": "https://github.com/example/repo"}}
824+
825+
# Test that get_info() retrieves the AgentView for the third agent
826+
info = await agents[2].get_info()
827+
assert info.id == "agent_003"
828+
assert info.name == "third-agent"
829+
assert info.create_time_ms == 1234567892000
830+
assert info.is_public is False
831+
assert info.source == {"type": "npm", "npm": {"package_name": "example-package"}}
832+
833+
# Verify that agents.retrieve was called three times (once for each get_info)
834+
assert mock_async_client.agents.retrieve.call_count == 3
835+
836+
mock_async_client.agents.list.assert_called_once()
837+
724838

725839
class TestAsyncRunloopSDK:
726840
"""Tests for AsyncRunloopSDK class."""
@@ -729,6 +843,7 @@ def test_init(self) -> None:
729843
"""Test AsyncRunloopSDK initialization."""
730844
sdk = AsyncRunloopSDK(bearer_token="test-token")
731845
assert sdk.api is not None
846+
assert isinstance(sdk.agent, AsyncAgentOps)
732847
assert isinstance(sdk.devbox, AsyncDevboxOps)
733848
assert isinstance(sdk.scorer, AsyncScorerOps)
734849
assert isinstance(sdk.snapshot, AsyncSnapshotOps)

tests/smoketests/sdk/README.md

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
# SDK End-to-End Smoke Tests
22

3-
Comprehensive end-to-end tests for the object-oriented Python SDK (`runloop_api_client.sdk`). These tests run against the real Runloop API to validate critical workflows including devboxes, blueprints, snapshots, and storage objects.
3+
Comprehensive end-to-end tests for the object-oriented Python SDK (`runloop_api_client.sdk`). These tests run against the real Runloop API to validate critical workflows including agents, devboxes, blueprints, snapshots, and storage objects.
44

55
## Overview
66

77
The Python SDK provides both synchronous and asynchronous interfaces:
8-
- **Synchronous SDK**: `RunloopSDK` with `Devbox`, `Blueprint`, `Snapshot`, `StorageObject`
9-
- **Asynchronous SDK**: `AsyncRunloopSDK` with `AsyncDevbox`, `AsyncBlueprint`, `AsyncSnapshot`, `AsyncStorageObject`
8+
- **Synchronous SDK**: `RunloopSDK` with `Agent`, `Devbox`, `Blueprint`, `Snapshot`, `StorageObject`
9+
- **Asynchronous SDK**: `AsyncRunloopSDK` with `AsyncAgent`, `AsyncDevbox`, `AsyncBlueprint`, `AsyncSnapshot`, `AsyncStorageObject`
1010

1111
These tests ensure both interfaces work correctly in real-world scenarios.
1212

@@ -15,6 +15,17 @@ These tests ensure both interfaces work correctly in real-world scenarios.
1515
### Infrastructure
1616
- `conftest.py` - Pytest fixtures for SDK client instances
1717

18+
### Agent Tests
19+
- `test_agent.py` - Synchronous agent operations
20+
- `test_async_agent.py` - Asynchronous agent operations
21+
22+
**Test Coverage:**
23+
- Agent lifecycle (create, get_info)
24+
- Agent listing and retrieval
25+
- Agent creation with metadata
26+
- Agent creation with different source types (npm, git)
27+
- Public/private agent configuration
28+
1829
### Devbox Tests
1930
- `test_devbox.py` - Synchronous devbox operations
2031
- `test_async_devbox.py` - Asynchronous devbox operations
@@ -74,23 +85,30 @@ export RUNLOOP_API_KEY=your_api_key_here
7485

7586
### Run All SDK Smoke Tests
7687
```bash
77-
RUN_SMOKETESTS=1 uv run pytest -q -vv -m smoketest tests/smoketests/sdk/
88+
RUN_SMOKETESTS=1 uv run pytest -q -vv -n 20 -m smoketest tests/smoketests/sdk/
7889
```
7990

8091
### Run Specific Test File
8192
```bash
82-
RUN_SMOKETESTS=1 uv run pytest -q -vv -m smoketest tests/smoketests/sdk/test_devbox.py
93+
RUN_SMOKETESTS=1 uv run pytest -q -vv -n 20 -m smoketest tests/smoketests/sdk/test_devbox.py
8394
```
8495

8596
### Run Specific Test
8697
```bash
87-
RUN_SMOKETESTS=1 uv run pytest -q -vv -m smoketest -k "test_devbox_lifecycle" tests/smoketests/sdk/
98+
# match a test from anywhere in the directory
99+
RUN_SMOKETESTS=1 uv run pytest -q -vv-m smoketest -k "test_devbox_lifecycle" tests/smoketests/sdk/
100+
101+
# specify the exact test
102+
RUN_SMOKETESTS=1 uv run pytest -q -vv -m smoketest tests/smoketests/sdk/test_devbox.py::TestDevboxLifecycle::test_devbox_create
103+
104+
# match a test from one file
105+
RUN_SMOKETESTS=1 uv run pytest -q -vv -m smoketest -k "test_devbox_create" tests/smoketests/sdk/test_devbox.py
88106
```
89107

90108
### Run Only Sync or Async Tests
91109
```bash
92110
# Sync tests only (files without 'async' prefix)
93-
RUN_SMOKETESTS=1 uv run pytest -q -vv -m smoketest tests/smoketests/sdk/test_devbox.py tests/smoketests/sdk/test_blueprint.py tests/smoketests/sdk/test_snapshot.py tests/smoketests/sdk/test_storage_object.py
111+
RUN_SMOKETESTS=1 uv run pytest -q -vv -m smoketest $(ls tests/smoketests/sdk/*.py | grep -v async)
94112

95113
# Async tests only (files with 'async' prefix)
96114
RUN_SMOKETESTS=1 uv run pytest -q -vv -m smoketest tests/smoketests/sdk/test_async_*.py

0 commit comments

Comments
 (0)