Skip to content

Commit 126475e

Browse files
author
Datata1
committed
test(suite): set up initial testsuite
1 parent 691e3e9 commit 126475e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+3390
-180
lines changed

.env.example

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,6 @@
1-
CS_TOKEN="secret_token"
1+
# Required: Your Codesphere API token
2+
# Get this from your Codesphere account settings
3+
CS_TOKEN=your-api-token-here
4+
# CS_BASE_URL=https://codesphere.com/api
5+
# CS_TEST_TEAM_ID=12345
6+
# CS_TEST_DC_ID=1

.github/copilot-instructions.md

Lines changed: 140 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,17 @@ src/codesphere/
3030
└── pipeline/ # (Placeholder)
3131
3232
tests/ # Test files mirroring src structure
33+
├── conftest.py # Shared unit test fixtures
34+
├── core/ # Core infrastructure tests
35+
├── resources/ # Resource unit tests
36+
└── integration/ # Integration tests (real API)
37+
├── conftest.py # Integration fixtures & workspace setup
38+
├── test_domains.py
39+
├── test_env_vars.py
40+
├── test_metadata.py
41+
├── test_teams.py
42+
└── test_workspaces.py
43+
3344
examples/ # Usage examples organized by resource type
3445
```
3546

@@ -67,7 +78,7 @@ _GET_OP = APIOperation(
6778

6879
# Example resource method
6980
async def get(self, resource_id: int) -> ResourceModel:
70-
return await self.get_op(data=resource_id)
81+
return await self.get_op(resource_id=resource_id)
7182
```
7283

7384
### Model Guidelines
@@ -91,26 +102,141 @@ class Workspace(WorkspaceBase, _APIOperationExecutor):
91102
- Raise `RuntimeError` for SDK misuse (e.g., accessing resources without context manager)
92103
- Use custom exceptions from `exceptions.py` for SDK-specific errors
93104

94-
### Testing
95-
96-
- Use `pytest.mark.asyncio` for async tests
97-
- Use `@dataclass` for test case definitions with parametrization
98-
- Mock `httpx.AsyncClient` for HTTP request testing
99-
- Test files should mirror the source structure in `tests/`
100-
101105
### Code Style
102106

103107
- Line length: 88 characters (Ruff/Black standard)
104108
- Indentation: 4 spaces
105109
- Quotes: Double quotes for strings
106110
- Imports: Group stdlib, third-party, and local imports
107111

108-
### Development Commands
112+
---
113+
114+
## Testing Guidelines
115+
116+
When adding features or making changes, appropriate tests are **required**. The SDK uses two types of tests:
117+
118+
### Unit Tests
119+
120+
Located in `tests/` (excluding `tests/integration/`). These mock HTTP responses and test SDK logic in isolation.
121+
122+
**When to write unit tests:**
123+
- New Pydantic models or schemas
124+
- New API operations
125+
- Core handler or utility logic changes
126+
127+
**Unit test patterns:**
128+
129+
```python
130+
import pytest
131+
from dataclasses import dataclass
132+
from unittest.mock import AsyncMock, MagicMock
133+
134+
# Use @dataclass for parameterized test cases
135+
@dataclass
136+
class WorkspaceTestCase:
137+
name: str
138+
workspace_id: int
139+
expected_name: str
140+
141+
@pytest.mark.asyncio
142+
@pytest.mark.parametrize("case", [
143+
WorkspaceTestCase(name="basic", workspace_id=123, expected_name="test-ws"),
144+
])
145+
async def test_workspace_get(case: WorkspaceTestCase):
146+
"""Should fetch a workspace by ID."""
147+
mock_response = MagicMock()
148+
mock_response.json.return_value = {"id": case.workspace_id, "name": case.expected_name}
149+
150+
# Test implementation...
151+
```
152+
153+
### Integration Tests
154+
155+
Located in `tests/integration/`. These run against the real Codesphere API.
156+
157+
**When to write integration tests:**
158+
- New API endpoints (CRUD operations)
159+
- Changes to request/response serialization
160+
- Schema field changes (detect API contract changes early)
161+
162+
**Integration test patterns:**
163+
164+
```python
165+
import pytest
166+
from codesphere import CodesphereSDK
167+
168+
pytestmark = [pytest.mark.integration, pytest.mark.asyncio]
169+
170+
class TestMyResourceIntegration:
171+
"""Integration tests for MyResource endpoints."""
172+
173+
async def test_list_resources(self, sdk_client: CodesphereSDK):
174+
"""Should retrieve a list of resources."""
175+
resources = await sdk_client.my_resource.list()
176+
177+
assert isinstance(resources, list)
178+
179+
async def test_create_and_delete(
180+
self,
181+
sdk_client: CodesphereSDK,
182+
test_team_id: int,
183+
):
184+
"""Should create and delete a resource."""
185+
resource = await sdk_client.my_resource.create(name="test")
186+
187+
try:
188+
assert resource.name == "test"
189+
finally:
190+
# Always cleanup created resources
191+
await resource.delete()
192+
```
193+
194+
**Available integration test fixtures** (from `tests/integration/conftest.py`):
195+
196+
| Fixture | Scope | Description |
197+
|---------|-------|-------------|
198+
| `sdk_client` | function | Fresh SDK client for each test |
199+
| `session_sdk_client` | session | Shared SDK client for setup/teardown |
200+
| `test_team_id` | session | Team ID for testing |
201+
| `test_workspace` | session | Single pre-created workspace |
202+
| `test_workspaces` | session | List of 2 test workspaces |
203+
| `integration_token` | session | The API token (from `CS_TOKEN`) |
204+
205+
**Environment variables:**
206+
207+
| Variable | Required | Description |
208+
|----------|----------|-------------|
209+
| `CS_TOKEN` | Yes | Codesphere API token |
210+
| `CS_TEST_TEAM_ID` | No | Specific team ID (defaults to first team) |
211+
| `CS_TEST_DC_ID` | No | Datacenter ID (defaults to 1) |
212+
213+
### Running Tests
214+
215+
```bash
216+
make test # Run unit tests only
217+
make test-unit # Run unit tests only (explicit)
218+
make test-integration # Run integration tests (requires CS_TOKEN)
219+
```
220+
221+
### Test Requirements Checklist
222+
223+
When submitting a PR, ensure:
224+
225+
- [ ] **New endpoints** have integration tests covering all operations
226+
- [ ] **New models** have unit tests for serialization/deserialization
227+
- [ ] **Bug fixes** include a test that reproduces the issue
228+
- [ ] **All tests pass** locally before pushing
229+
230+
---
231+
232+
## Development Commands
109233

110234
```bash
111-
make install # Set up development environment
112-
make lint # Run Ruff linter
113-
make format # Format code with Ruff
114-
make test # Run pytest
115-
make commit # Guided commit with Commitizen
235+
make install # Set up development environment
236+
make lint # Run Ruff linter
237+
make format # Format code with Ruff
238+
make test # Run unit tests
239+
make test-unit # Run unit tests (excludes integration)
240+
make test-integration # Run integration tests
241+
make commit # Guided commit with Commitizen
116242
```

.github/workflows/integration.yml

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
name: Integration Tests
2+
3+
on:
4+
# Manual trigger with optional inputs
5+
workflow_dispatch:
6+
inputs:
7+
test_team_id:
8+
description: "Team ID to use for testing (optional)"
9+
required: false
10+
type: string
11+
test_dc_id:
12+
description: "Datacenter ID for test resources"
13+
required: false
14+
default: "1"
15+
type: string
16+
17+
# Run on pull requests
18+
pull_request:
19+
types: [opened, synchronize, reopened, ready_for_review]
20+
paths:
21+
- "src/codesphere/**"
22+
- "tests/integration/**"
23+
- ".github/workflows/integration.yml"
24+
25+
permissions:
26+
contents: read
27+
28+
env:
29+
PYTHON_VERSION: "3.12"
30+
31+
jobs:
32+
integration-tests:
33+
name: Run Integration Tests
34+
runs-on: ubuntu-latest
35+
# Only run if the secret is available (prevents failures on forks)
36+
if: ${{ github.repository == 'Datata1/codesphere-python' || github.event_name == 'workflow_dispatch' }}
37+
38+
steps:
39+
- name: Checkout repository
40+
uses: actions/checkout@v4
41+
42+
- name: Install uv package manager
43+
uses: astral-sh/setup-uv@v6
44+
with:
45+
activate-environment: true
46+
47+
- name: Install dependencies
48+
run: uv sync --extra dev
49+
50+
- name: Run integration tests
51+
env:
52+
CS_TOKEN: ${{ secrets.CS_TOKEN }}
53+
CS_TEST_TEAM_ID: ${{ inputs.test_team_id || secrets.CS_TEST_TEAM_ID }}
54+
CS_TEST_DC_ID: ${{ inputs.test_dc_id || '1' }}
55+
run: |
56+
echo "Running integration tests..."
57+
uv run pytest tests/integration -v --run-integration \
58+
--junitxml=junit/integration-results.xml \
59+
--tb=short
60+
61+
- name: Upload test results
62+
uses: actions/upload-artifact@v4
63+
if: always()
64+
with:
65+
name: integration-test-results
66+
path: junit/integration-results.xml
67+
retention-days: 30
68+
69+
- name: Minimize uv cache
70+
run: uv cache prune --ci
71+
72+
integration-tests-summary:
73+
name: Integration Tests Summary
74+
runs-on: ubuntu-latest
75+
needs: integration-tests
76+
if: always()
77+
78+
steps:
79+
- name: Download test results
80+
uses: actions/download-artifact@v4
81+
with:
82+
name: integration-test-results
83+
path: junit
84+
85+
- name: Publish Test Results
86+
uses: EnricoMi/publish-unit-test-result-action@v2
87+
if: always()
88+
with:
89+
files: junit/integration-results.xml
90+
check_name: Integration Test Results
91+
comment_mode: off

0 commit comments

Comments
 (0)