Skip to content

Commit c323240

Browse files
committed
add some tests; not working yet
1 parent c879e7d commit c323240

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_clients.py

Lines changed: 116 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,17 @@
1515
MockObjectView,
1616
MockSnapshotView,
1717
MockBlueprintView,
18+
MockAgentView,
1819
create_mock_httpx_response,
1920
)
20-
from runloop_api_client.sdk import AsyncDevbox, AsyncSnapshot, AsyncBlueprint, AsyncStorageObject
21+
from runloop_api_client.sdk import AsyncDevbox, AsyncSnapshot, AsyncBlueprint, AsyncStorageObject, AsyncAgent
2122
from runloop_api_client.sdk.async_ import (
2223
AsyncDevboxOps,
2324
AsyncRunloopSDK,
2425
AsyncSnapshotOps,
2526
AsyncBlueprintOps,
2627
AsyncStorageObjectOps,
28+
AsyncAgentOps,
2729
)
2830
from runloop_api_client.lib.polling import PollingConfig
2931

@@ -515,13 +517,126 @@ async def test_upload_from_dir_with_string_path(
515517
mock_async_client.objects.complete.assert_awaited_once()
516518

517519

520+
class TestAsyncAgentClient:
521+
"""Tests for AsyncAgentClient class."""
522+
523+
@pytest.mark.asyncio
524+
async def test_create(self, mock_async_client: AsyncMock, agent_view: MockAgentView) -> None:
525+
"""Test create method."""
526+
mock_async_client.agents.create = AsyncMock(return_value=agent_view)
527+
528+
client = AsyncAgentOps(mock_async_client)
529+
agent = await client.create(
530+
name="test-agent",
531+
metadata={"key": "value"},
532+
)
533+
534+
assert isinstance(agent, AsyncAgent)
535+
assert agent.id == "agent_123"
536+
mock_async_client.agents.create.assert_called_once()
537+
538+
def test_from_id(self, mock_async_client: AsyncMock) -> None:
539+
"""Test from_id method."""
540+
client = AsyncAgentOps(mock_async_client)
541+
agent = client.from_id("agent_123")
542+
543+
assert isinstance(agent, AsyncAgent)
544+
assert agent.id == "agent_123"
545+
546+
@pytest.mark.asyncio
547+
async def test_list(self, mock_async_client: AsyncMock) -> None:
548+
"""Test list method."""
549+
# Create three agent views with different data
550+
agent_view_1 = MockAgentView(
551+
id="agent_001",
552+
name="first-agent",
553+
create_time_ms=1234567890000,
554+
is_public=False,
555+
source=None,
556+
)
557+
agent_view_2 = MockAgentView(
558+
id="agent_002",
559+
name="second-agent",
560+
create_time_ms=1234567891000,
561+
is_public=True,
562+
source={"type": "git", "git": {"repository": "https://github.com/example/repo"}},
563+
)
564+
agent_view_3 = MockAgentView(
565+
id="agent_003",
566+
name="third-agent",
567+
create_time_ms=1234567892000,
568+
is_public=False,
569+
source={"type": "npm", "npm": {"package_name": "example-package"}},
570+
)
571+
572+
page = SimpleNamespace(agents=[agent_view_1, agent_view_2, agent_view_3])
573+
mock_async_client.agents.list = AsyncMock(return_value=page)
574+
575+
# Mock retrieve to return the corresponding agent_view when called
576+
async def mock_retrieve(agent_id, **kwargs):
577+
if agent_id == "agent_001":
578+
return agent_view_1
579+
elif agent_id == "agent_002":
580+
return agent_view_2
581+
elif agent_id == "agent_003":
582+
return agent_view_3
583+
return None
584+
585+
mock_async_client.agents.retrieve = AsyncMock(side_effect=mock_retrieve)
586+
587+
client = AsyncAgentOps(mock_async_client)
588+
agents = await client.list(
589+
limit=10,
590+
starting_after="agent_000",
591+
)
592+
593+
# Verify we got three agents
594+
assert len(agents) == 3
595+
assert all(isinstance(agent, AsyncAgent) for agent in agents)
596+
597+
# Verify the agent IDs
598+
assert agents[0].id == "agent_001"
599+
assert agents[1].id == "agent_002"
600+
assert agents[2].id == "agent_003"
601+
602+
# Test that get_info() retrieves the AgentView for the first agent
603+
info = await agents[0].get_info()
604+
assert info.id == "agent_001"
605+
assert info.name == "first-agent"
606+
assert info.create_time_ms == 1234567890000
607+
assert info.is_public is False
608+
assert info.source is None
609+
610+
# Test that get_info() retrieves the AgentView for the second agent
611+
info = await agents[1].get_info()
612+
assert info.id == "agent_002"
613+
assert info.name == "second-agent"
614+
assert info.create_time_ms == 1234567891000
615+
assert info.is_public is True
616+
assert info.source == {"type": "git", "git": {"repository": "https://github.com/example/repo"}}
617+
618+
# Test that get_info() retrieves the AgentView for the third agent
619+
info = await agents[2].get_info()
620+
assert info.id == "agent_003"
621+
assert info.name == "third-agent"
622+
assert info.create_time_ms == 1234567892000
623+
assert info.is_public is False
624+
assert info.source == {"type": "npm", "npm": {"package_name": "example-package"}}
625+
626+
# Verify that agents.retrieve was called three times (once for each get_info)
627+
assert mock_async_client.agents.retrieve.call_count == 3
628+
629+
mock_async_client.agents.list.assert_called_once()
630+
631+
518632
class TestAsyncRunloopSDK:
519633
"""Tests for AsyncRunloopSDK class."""
520634

521635
def test_init(self) -> None:
522636
"""Test AsyncRunloopSDK initialization."""
523637
sdk = AsyncRunloopSDK(bearer_token="test-token")
524638
assert sdk.api is not None
639+
assert isinstance(sdk.agent, AsyncAgentOps)
525640
assert isinstance(sdk.devbox, AsyncDevboxOps)
526641
assert isinstance(sdk.snapshot, AsyncSnapshotOps)
527642
assert isinstance(sdk.blueprint, AsyncBlueprintOps)

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)