Skip to content

Commit 8d42324

Browse files
committed
add sdk tests and enable in actions workflow
1 parent 128dc5e commit 8d42324

File tree

11 files changed

+922
-5
lines changed

11 files changed

+922
-5
lines changed

.github/workflows/sdk-tests.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: SDK Tests
2+
3+
on:
4+
push:
5+
branches: [dev, main]
6+
pull_request:
7+
branches: [dev, main]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
matrix:
14+
python-version: ["3.9", "3.10", "3.11"]
15+
16+
steps:
17+
- uses: actions/checkout@v3
18+
19+
- name: Set up Python ${{ matrix.python-version }}
20+
uses: actions/setup-python@v4
21+
with:
22+
python-version: ${{ matrix.python-version }}
23+
24+
- name: Install dependencies
25+
run: |
26+
python -m pip install --upgrade pip
27+
pip install -e ".[dev]"
28+
29+
- name: Run type checking with mypy
30+
run: |
31+
mypy dataspace_sdk/ --ignore-missing-imports
32+
33+
- name: Run tests with pytest
34+
run: |
35+
pytest tests/ -v --cov=dataspace_sdk --cov-report=term-missing --cov-report=xml
36+
37+
- name: Upload coverage to Codecov
38+
uses: codecov/codecov-action@v3
39+
with:
40+
file: ./coverage.xml
41+
flags: unittests
42+
name: codecov-umbrella
43+
fail_ci_if_error: false

docs/sdk/DEVELOPMENT.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,18 +85,26 @@ datasets = client.datasets.search(query="test")
8585
print(datasets)
8686
```
8787

88-
### Unit Tests (To Be Implemented)
88+
### Unit Tests
8989

90-
Create tests in `tests/` directory:
90+
The SDK includes comprehensive unit tests in the `tests/` directory:
9191

9292
```bash
93-
# Run tests
94-
pytest
93+
# Run all SDK tests (recommended - uses current Python environment)
94+
python run_sdk_tests.py
95+
96+
# Run specific test files
97+
python -m pytest tests/test_auth.py -v
98+
python -m pytest tests/test_datasets.py -v
99+
python -m pytest tests/test_aimodels.py -v
100+
python -m pytest tests/test_usecases.py -v
95101

96102
# Run with coverage
97-
pytest --cov=dataspace_sdk --cov-report=html
103+
python -m pytest tests/ --cov=dataspace_sdk --cov-report=html
98104
```
99105

106+
See [QUICK_TEST_GUIDE.md](QUICK_TEST_GUIDE.md) for more testing options.
107+
100108
## Code Quality
101109

102110
### Formatting

docs/sdk/QUICK_TEST_GUIDE.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Quick Test Guide
2+
3+
## Setup (One Time)
4+
5+
```bash
6+
# Install SDK with dev dependencies
7+
pip install -e ".[dev]"
8+
```
9+
10+
## Run Tests
11+
12+
```bash
13+
# Run all SDK tests (recommended - uses current Python environment)
14+
python run_sdk_tests.py
15+
16+
# Or run specific tests
17+
python -m pytest tests/test_auth.py -v
18+
python -m pytest tests/test_datasets.py -v
19+
python -m pytest tests/test_aimodels.py -v
20+
python -m pytest tests/test_usecases.py -v
21+
```
22+
23+
## Check Coverage
24+
25+
```bash
26+
python -m pytest tests/ --cov=dataspace_sdk --cov-report=term-missing
27+
```
28+
29+
## Type Checking
30+
31+
```bash
32+
mypy dataspace_sdk/ --ignore-missing-imports
33+
```
34+
35+
## CI/CD
36+
37+
Tests run automatically on:
38+
- Push to `dev` or `main`
39+
- Pull requests to `dev` or `main`
40+
41+
Check results in GitHub Actions tab.

run_sdk_tests.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
#!/usr/bin/env python
2+
"""Script to run SDK tests in the current Python environment."""
3+
4+
import subprocess
5+
import sys
6+
7+
8+
def check_dependencies() -> bool:
9+
"""Check if required dependencies are installed."""
10+
try:
11+
import mypy # type: ignore
12+
import pytest # type: ignore
13+
14+
return True
15+
except ImportError as e:
16+
print(f"\n❌ Missing dependency: {e.name}")
17+
print("\nPlease install dev dependencies:")
18+
print(' pip install -e ".[dev]"')
19+
return False
20+
21+
22+
def has_coverage() -> bool:
23+
"""Check if pytest-cov is installed."""
24+
try:
25+
import pytest_cov # type: ignore
26+
27+
return True
28+
except ImportError:
29+
return False
30+
31+
32+
def run_command(description: str, command: list[str]) -> None:
33+
"""Run a command and handle errors."""
34+
print(f"\n{description}")
35+
print("-" * 60)
36+
37+
try:
38+
result = subprocess.run(command, check=True, capture_output=False, text=True)
39+
except subprocess.CalledProcessError as e:
40+
print(f"\n❌ Error: {description} failed")
41+
sys.exit(1)
42+
except FileNotFoundError:
43+
print(f"\n❌ Error: Command not found. Please install dev dependencies:")
44+
print(' pip install -e ".[dev]"')
45+
sys.exit(1)
46+
47+
48+
def main() -> None:
49+
"""Run all SDK tests."""
50+
print("=" * 60)
51+
print("Running DataSpace SDK Tests")
52+
print("=" * 60)
53+
print(f"Python: {sys.version}")
54+
print(f"Executable: {sys.executable}")
55+
print("=" * 60)
56+
57+
# Check dependencies
58+
if not check_dependencies():
59+
sys.exit(1)
60+
61+
# Run type checking
62+
run_command(
63+
"1. Running mypy type checking...",
64+
[sys.executable, "-m", "mypy", "dataspace_sdk/", "--ignore-missing-imports"],
65+
)
66+
67+
# Run SDK-specific tests (without Django)
68+
test_files = [
69+
"tests/test_auth.py",
70+
"tests/test_base.py",
71+
"tests/test_client.py",
72+
"tests/test_datasets.py",
73+
"tests/test_aimodels.py",
74+
"tests/test_usecases.py",
75+
"tests/test_exceptions.py",
76+
]
77+
78+
# Build pytest command
79+
pytest_cmd = [
80+
sys.executable,
81+
"-m",
82+
"pytest",
83+
*test_files,
84+
"-v",
85+
"-p",
86+
"no:django", # Disable Django plugin for SDK tests
87+
"-o",
88+
"addopts=", # Override addopts from pytest.ini to remove --nomigrations
89+
"--override-ini=django_find_project=false", # Disable Django project finding
90+
"--noconftest", # Don't load any conftest.py files
91+
]
92+
93+
pytest_cmd.extend(["--cov=dataspace_sdk", "--cov-report=term-missing"])
94+
95+
run_command("2. Running SDK unit tests...", pytest_cmd)
96+
97+
print("\n" + "=" * 60)
98+
print("✅ All SDK tests passed!")
99+
print("=" * 60)
100+
101+
102+
if __name__ == "__main__":
103+
main()

tests/test_aimodels.py

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
"""Tests for AI model resource client."""
2+
3+
import unittest
4+
from unittest.mock import MagicMock, patch
5+
6+
from dataspace_sdk.resources.aimodels import AIModelClient
7+
8+
9+
class TestAIModelClient(unittest.TestCase):
10+
"""Test cases for AIModelClient."""
11+
12+
def setUp(self) -> None:
13+
"""Set up test fixtures."""
14+
self.base_url = "https://api.test.com"
15+
self.auth_client = MagicMock()
16+
self.client = AIModelClient(self.base_url, self.auth_client)
17+
18+
def test_init(self) -> None:
19+
"""Test AIModelClient initialization."""
20+
self.assertEqual(self.client.base_url, self.base_url)
21+
self.assertEqual(self.client.auth_client, self.auth_client)
22+
23+
@patch.object(AIModelClient, "get")
24+
def test_search_models(self, mock_get: MagicMock) -> None:
25+
"""Test AI model search."""
26+
mock_get.return_value = {
27+
"total": 5,
28+
"results": [{"id": "1", "displayName": "Test Model", "modelType": "LLM"}],
29+
}
30+
31+
result = self.client.search(query="test", page=1, page_size=10)
32+
33+
self.assertEqual(result["total"], 5)
34+
self.assertEqual(len(result["results"]), 1)
35+
self.assertEqual(result["results"][0]["displayName"], "Test Model")
36+
mock_get.assert_called_once()
37+
38+
@patch.object(AIModelClient, "get")
39+
def test_get_model_by_id(self, mock_get: MagicMock) -> None:
40+
"""Test get AI model by ID."""
41+
mock_get.return_value = {
42+
"id": "123",
43+
"displayName": "Test Model",
44+
"modelType": "LLM",
45+
"provider": "OpenAI",
46+
}
47+
48+
result = self.client.get_by_id("123")
49+
50+
self.assertEqual(result["id"], "123")
51+
self.assertEqual(result["displayName"], "Test Model")
52+
mock_get.assert_called_once()
53+
54+
@patch.object(AIModelClient, "post")
55+
def test_get_model_by_id_graphql(self, mock_post: MagicMock) -> None:
56+
"""Test get AI model by ID using GraphQL."""
57+
mock_post.return_value = {
58+
"data": {
59+
"aiModel": {
60+
"id": "123",
61+
"displayName": "Test Model",
62+
"description": "A test model",
63+
}
64+
}
65+
}
66+
67+
result = self.client.get_by_id_graphql("123")
68+
69+
self.assertEqual(result["id"], "123")
70+
self.assertEqual(result["displayName"], "Test Model")
71+
mock_post.assert_called_once()
72+
73+
@patch.object(AIModelClient, "post")
74+
def test_list_all_models(self, mock_post: MagicMock) -> None:
75+
"""Test list all AI models."""
76+
mock_post.return_value = {"data": {"aiModels": [{"id": "1", "displayName": "Model 1"}]}}
77+
78+
result = self.client.list_all(limit=10, offset=0)
79+
80+
self.assertIsInstance(result, (list, dict))
81+
mock_post.assert_called_once()
82+
83+
@patch.object(AIModelClient, "post")
84+
def test_get_organization_models(self, mock_post: MagicMock) -> None:
85+
"""Test get organization AI models."""
86+
mock_post.return_value = {"data": {"aiModels": [{"id": "1", "name": "Org Model"}]}}
87+
88+
result = self.client.get_organization_models("org-123", limit=10)
89+
90+
self.assertIsInstance(result, (list, dict))
91+
mock_post.assert_called_once()
92+
93+
@patch.object(AIModelClient, "get")
94+
def test_search_with_filters(self, mock_get: MagicMock) -> None:
95+
"""Test AI model search with filters."""
96+
mock_get.return_value = {"total": 3, "results": []}
97+
98+
result = self.client.search(
99+
query="language",
100+
tags=["nlp"],
101+
sectors=["tech"],
102+
model_type="LLM",
103+
provider="OpenAI",
104+
status="ACTIVE",
105+
)
106+
107+
self.assertEqual(result["total"], 3)
108+
mock_get.assert_called_once()
109+
110+
@patch.object(AIModelClient, "post")
111+
def test_graphql_error_handling(self, mock_post: MagicMock) -> None:
112+
"""Test GraphQL error handling."""
113+
from dataspace_sdk.exceptions import DataSpaceAPIError
114+
115+
mock_post.return_value = {"errors": [{"message": "GraphQL error"}]}
116+
117+
with self.assertRaises(DataSpaceAPIError):
118+
self.client.get_by_id_graphql("123")
119+
120+
121+
if __name__ == "__main__":
122+
unittest.main()

0 commit comments

Comments
 (0)