Skip to content

Commit b5e7888

Browse files
authored
Merge pull request #24 from VectorInstitute/add_tests
Add version command, unit tests
2 parents 9e92817 + 14a911d commit b5e7888

File tree

7 files changed

+1443
-0
lines changed

7 files changed

+1443
-0
lines changed

.github/workflows/unit_tests.yml

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
name: unit tests
2+
permissions:
3+
contents: read
4+
pull-requests: write
5+
6+
on:
7+
push:
8+
branches:
9+
- main
10+
paths:
11+
- .pre-commit-config.yaml
12+
- .github/workflows/code_checks.yml
13+
- .github/workflows/docs.yml
14+
- .github/workflows/unit_tests.yml
15+
- '**.py'
16+
- '**.ipynb'
17+
- uv.lock
18+
- pyproject.toml
19+
- '**.rst'
20+
- '**.md'
21+
pull_request:
22+
branches:
23+
- main
24+
paths:
25+
- .pre-commit-config.yaml
26+
- .github/workflows/code_checks.yml
27+
- .github/workflows/docs.yml
28+
- .github/workflows/unit_tests.yml
29+
- '**.py'
30+
- '**.ipynb'
31+
- uv.lock
32+
- pyproject.toml
33+
- '**.rst'
34+
- '**.md'
35+
36+
jobs:
37+
unit-tests:
38+
runs-on: ubuntu-latest
39+
steps:
40+
- uses: actions/[email protected]
41+
42+
- name: Install uv
43+
uses: astral-sh/setup-uv@b75a909f75acd358c2196fb9a5f1299a9a8868a4
44+
with:
45+
# Install a specific version of uv.
46+
version: "0.9.7"
47+
enable-cache: true
48+
49+
- name: "Set up Python"
50+
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c
51+
with:
52+
python-version-file: ".python-version"
53+
54+
- name: Install the project
55+
run: uv sync --all-extras --dev
56+
57+
- name: Install dependencies and check code
58+
run: |
59+
uv run pytest -m "not integration_test" --cov src/aieng_platform_onboard --cov-report=xml tests
60+
61+
# Uncomment this once this repo is configured on Codecov
62+
- name: Upload coverage to Codecov
63+
uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7
64+
with:
65+
token: ${{ secrets.CODECOV_TOKEN }}
66+
slug: VectorInstitute/aieng-platform
67+
fail_ci_if_error: true
68+
verbose: true

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,4 @@ terraform.tfvars
3131

3232
.coder
3333
keys/
34+
.coverage

src/aieng_platform_onboard/cli.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import argparse
1212
import subprocess
1313
import sys
14+
from importlib.metadata import version
1415
from pathlib import Path
1516
from typing import Any
1617

@@ -34,6 +35,21 @@
3435
)
3536

3637

38+
def get_version() -> str:
39+
"""
40+
Get the installed version of the package.
41+
42+
Returns
43+
-------
44+
str
45+
Version string from package metadata.
46+
"""
47+
try:
48+
return version("aieng-platform-onboard")
49+
except Exception:
50+
return "unknown"
51+
52+
3753
def run_integration_test(test_script: Path) -> tuple[bool, str]:
3854
"""
3955
Execute integration test script to verify API keys.
@@ -455,6 +471,12 @@ def main() -> int: # noqa: PLR0911
455471
description="Bootcamp participant onboarding script",
456472
formatter_class=argparse.RawDescriptionHelpFormatter,
457473
)
474+
parser.add_argument(
475+
"--version",
476+
action="version",
477+
version=f"%(prog)s {get_version()}",
478+
help="Show version number and exit",
479+
)
458480
parser.add_argument(
459481
"--bootcamp-name",
460482
type=str,

tests/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Tests for aieng_platform_onboard package."""

tests/conftest.py

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
"""Pytest configuration and fixtures for aieng_platform_onboard tests."""
2+
3+
from datetime import datetime, timezone
4+
from typing import Any
5+
from unittest.mock import Mock
6+
7+
import pytest
8+
from google.cloud import firestore
9+
10+
11+
@pytest.fixture
12+
def mock_firestore_client() -> Mock:
13+
"""
14+
Create a mock Firestore client for testing.
15+
16+
Returns
17+
-------
18+
Mock
19+
Mock Firestore client instance.
20+
"""
21+
return Mock(spec=firestore.Client)
22+
23+
24+
@pytest.fixture
25+
def mock_firestore_document() -> Mock:
26+
"""
27+
Create a mock Firestore document for testing.
28+
29+
Returns
30+
-------
31+
Mock
32+
Mock Firestore document instance.
33+
"""
34+
mock_doc = Mock()
35+
mock_doc.exists = True
36+
mock_doc.id = "test-handle"
37+
mock_doc.to_dict.return_value = {
38+
"github_handle": "test-handle",
39+
"team_name": "test-team",
40+
"onboarded": False,
41+
"email": "[email protected]",
42+
}
43+
return mock_doc
44+
45+
46+
@pytest.fixture
47+
def sample_participant_data() -> dict[str, Any]:
48+
"""
49+
Sample participant data for testing.
50+
51+
Returns
52+
-------
53+
dict[str, Any]
54+
Sample participant data dictionary.
55+
"""
56+
return {
57+
"github_handle": "test-user",
58+
"team_name": "test-team",
59+
"onboarded": False,
60+
"email": "[email protected]",
61+
"created_at": datetime.now(timezone.utc),
62+
}
63+
64+
65+
@pytest.fixture
66+
def sample_team_data() -> dict[str, Any]:
67+
"""
68+
Sample team data for testing.
69+
70+
Returns
71+
-------
72+
dict[str, Any]
73+
Sample team data dictionary.
74+
"""
75+
return {
76+
"team_name": "test-team",
77+
"openai_api_key": "test-openai-key",
78+
"langfuse_secret_key": "test-langfuse-secret",
79+
"langfuse_public_key": "test-langfuse-public",
80+
"langfuse_url": "https://test-langfuse.example.com",
81+
"web_search_api_key": "test-search-key",
82+
"participants": ["test-user", "test-user-2"],
83+
}
84+
85+
86+
@pytest.fixture
87+
def sample_global_keys() -> dict[str, Any]:
88+
"""
89+
Sample global keys for testing.
90+
91+
Returns
92+
-------
93+
dict[str, Any]
94+
Sample global keys dictionary.
95+
"""
96+
return {
97+
"EMBEDDING_BASE_URL": "https://embedding.example.com",
98+
"EMBEDDING_API_KEY": "test-embedding-key",
99+
"WEAVIATE_HTTP_HOST": "weaviate.example.com",
100+
"WEAVIATE_GRPC_HOST": "weaviate-grpc.example.com",
101+
"WEAVIATE_API_KEY": "test-weaviate-key",
102+
"WEAVIATE_HTTP_PORT": "443",
103+
"WEAVIATE_GRPC_PORT": "50051",
104+
"WEAVIATE_HTTP_SECURE": "true",
105+
"WEAVIATE_GRPC_SECURE": "true",
106+
}
107+
108+
109+
@pytest.fixture
110+
def mock_requests_post(monkeypatch: pytest.MonkeyPatch) -> Mock:
111+
"""
112+
Mock requests.post for testing HTTP requests.
113+
114+
Parameters
115+
----------
116+
monkeypatch : pytest.MonkeyPatch
117+
Pytest monkeypatch fixture.
118+
119+
Returns
120+
-------
121+
Mock
122+
Mock requests.post function.
123+
"""
124+
mock_response = Mock()
125+
mock_response.status_code = 200
126+
mock_response.json.return_value = {"token": "test-token"}
127+
128+
mock_post = Mock(return_value=mock_response)
129+
monkeypatch.setattr("requests.post", mock_post)
130+
return mock_post
131+
132+
133+
@pytest.fixture
134+
def mock_google_auth(monkeypatch: pytest.MonkeyPatch) -> tuple[Mock, str]:
135+
"""
136+
Mock google.auth.default for testing authentication.
137+
138+
Parameters
139+
----------
140+
monkeypatch : pytest.MonkeyPatch
141+
Pytest monkeypatch fixture.
142+
143+
Returns
144+
-------
145+
tuple[Mock, str]
146+
Tuple of (mock_credentials, project_id).
147+
"""
148+
mock_credentials = Mock()
149+
mock_credentials.service_account_email = "[email protected]"
150+
mock_credentials.signer = Mock()
151+
152+
def mock_default() -> tuple[Mock, str]:
153+
return mock_credentials, "test-project"
154+
155+
monkeypatch.setattr("google.auth.default", mock_default)
156+
return mock_credentials, "test-project"
157+
158+
159+
@pytest.fixture
160+
def mock_subprocess_run(monkeypatch: pytest.MonkeyPatch) -> Mock:
161+
"""
162+
Mock subprocess.run for testing command execution.
163+
164+
Parameters
165+
----------
166+
monkeypatch : pytest.MonkeyPatch
167+
Pytest monkeypatch fixture.
168+
169+
Returns
170+
-------
171+
Mock
172+
Mock subprocess.run function.
173+
"""
174+
mock_result = Mock()
175+
mock_result.returncode = 0
176+
mock_result.stdout = "test output"
177+
mock_result.stderr = ""
178+
179+
mock_run = Mock(return_value=mock_result)
180+
monkeypatch.setattr("subprocess.run", mock_run)
181+
return mock_run
182+
183+
184+
@pytest.fixture
185+
def mock_console(monkeypatch: pytest.MonkeyPatch) -> Mock:
186+
"""
187+
Mock Rich console for testing output.
188+
189+
Parameters
190+
----------
191+
monkeypatch : pytest.MonkeyPatch
192+
Pytest monkeypatch fixture.
193+
194+
Returns
195+
-------
196+
Mock
197+
Mock console instance.
198+
"""
199+
mock_console_instance = Mock()
200+
monkeypatch.setattr("aieng_platform_onboard.utils.console", mock_console_instance)
201+
monkeypatch.setattr("aieng_platform_onboard.cli.console", mock_console_instance)
202+
return mock_console_instance

0 commit comments

Comments
 (0)