Skip to content

Commit 9d4a91c

Browse files
Merge main branch
2 parents c905487 + 71a037c commit 9d4a91c

File tree

11 files changed

+245
-12
lines changed

11 files changed

+245
-12
lines changed

.github/workflows/ci_cd.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,45 @@ jobs:
9595
token: ${{ secrets.GITHUB_TOKEN }}
9696

9797

98+
tests:
99+
name: "Tests"
100+
runs-on: ${{ matrix.os }}
101+
needs: [code-style]
102+
strategy:
103+
matrix:
104+
os: [ubuntu-latest, windows-latest]
105+
python-version: ['3.9', '3.12']
106+
fail-fast: false
107+
steps:
108+
- name: Checkout code
109+
uses: actions/checkout@v4
110+
111+
- name: Set up Python ${{ matrix.python-version }}
112+
uses: actions/setup-python@v5
113+
with:
114+
python-version: ${{ matrix.python-version }}
115+
116+
- name: Install dependencies
117+
run: |
118+
python -m pip install --upgrade pip
119+
pip install -r requirements.txt
120+
121+
- name: Testing
122+
uses: ansys/actions/tests-pytest@v6
123+
timeout-minutes: 12
124+
with:
125+
checkout: false
126+
skip-install: true
127+
pytest-extra-args: "--cov=ansys.allie.flowkit.python --cov-report=term --cov-report=html:.cov/html --cov-report=xml:.cov/coverage.xml"
128+
129+
- name: Upload coverage results (HTML)
130+
uses: actions/upload-artifact@v4
131+
if: (matrix.python-version == env.MAIN_PYTHON_VERSION) && (runner.os == 'Linux')
132+
with:
133+
name: coverage-html
134+
path: .cov/html
135+
retention-days: 7
136+
98137
release:
99138
name: "Release project"
100139
if: github.event_name == 'push' && contains(github.ref, 'refs/tags')

.pre-commit-config.yaml

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,6 @@ repos:
1313
- id: check-yaml
1414
- id: trailing-whitespace
1515

16-
#- repo: https://github.com/ansys/pre-commit-hooks
17-
# rev: v0.3.1
18-
# hooks:
19-
# - id: add-license-headers
20-
# files: '(src|examples|tests|docker)/.*\.(py)|\.(proto)'
21-
# args:
22-
# - --start_year=2024
23-
2416
- repo: https://github.com/python-jsonschema/check-jsonschema
2517
rev: 0.27.3
2618
hooks:

pyproject.toml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ build-backend = "flit_core.buildapi"
55
[project]
66
name = "allie-flowkit-python"
77
version = "0.1.dev0"
8-
description = ""
8+
description = "A Python service for Allie Flowkit."
99
readme = "README.md"
1010
requires-python = ">=3.10,<4"
1111
license = { file = "LICENSE" }
@@ -28,12 +28,19 @@ dependencies = [
2828
"pymupdf >= 1.24.9,<2",
2929
"python_pptx >= 0.6.23,<1",
3030
"PyYAML >= 6.0.1,<7",
31+
"httpx >= 0.27.0",
32+
3133
]
3234

3335
[project.optional-dependencies]
3436
all = [
3537
"uvicorn[standard] >= 0.30.5,<1",
3638
]
39+
40+
tests = [
41+
"pytest >= 8.3.2,<9",
42+
"pytest-cov >= 5.0.0,<6",
43+
]
3744
doc = [
3845
"ansys-sphinx-theme==0.16.6",
3946
"jupyter_sphinx==0.5.3",

setup.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from setuptools import find_packages, setup
2+
3+
setup(
4+
name="ansys-allie-flowkit-python",
5+
version="0.1.0",
6+
packages=find_packages(include=["app", "docker", "configs"]),
7+
)

src/allie/flowkit/fastapi_utils.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@ def get_parameters_info(params: dict):
100100
if isinstance(param.annotation, bytes):
101101
param_info = ParameterInfo(name=param.name, type="bytes")
102102
parameters_info.append(param_info)
103-
elif hasattr(param.annotation, "schema"):
104-
schema = param.annotation.schema()
103+
elif hasattr(param.annotation, "model_json_schema"):
104+
schema = param.annotation.model_json_schema()
105105
param_info = extract_fields_from_schema(schema)
106106
parameters_info.extend(param_info)
107107
else:
@@ -124,7 +124,7 @@ def get_return_type_info(return_type: Type[BaseModel]):
124124
A list of ParameterInfo objects representing the return type fields.
125125
126126
"""
127-
if hasattr(return_type, "schema"):
127+
if hasattr(return_type, "model_json_schema"):
128128
schema = return_type.model_json_schema()
129129
return extract_fields_from_schema(schema)
130130
return [ParameterInfo(name="return", type=str(return_type.__name__))]

tests/__init__.py

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

tests/conftest.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from unittest.mock import patch
2+
3+
import pytest
4+
5+
# Mock API key for testing
6+
MOCK_API_KEY = "test_api_key"
7+
8+
9+
@pytest.fixture(autouse=True)
10+
def mock_api_key():
11+
"""Mock the API key for testing."""
12+
with patch("app.config.CONFIG.flowkit_python_api_key", MOCK_API_KEY):
13+
yield

tests/test_endpoints_splitter.py

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import base64
2+
3+
from app.app import app
4+
from app.endpoints.splitter import validate_request
5+
from app.models.splitter import SplitterRequest
6+
from fastapi import HTTPException
7+
from fastapi.testclient import TestClient
8+
import pytest
9+
10+
from tests.conftest import MOCK_API_KEY
11+
12+
# Create a test client
13+
client = TestClient(app)
14+
15+
16+
def encode_file_to_base64(file_path):
17+
"""Encode a file to base64 string."""
18+
with open(file_path, "rb") as file:
19+
return base64.b64encode(file.read()).decode("utf-8")
20+
21+
22+
@pytest.mark.asyncio
23+
async def test_split_ppt():
24+
"""Test splitting text in a PowerPoint document into chunks."""
25+
ppt_content_base64 = encode_file_to_base64("./tests/test_files/test_presentation.pptx")
26+
request_payload = {
27+
"document_content": ppt_content_base64,
28+
"chunk_size": 100,
29+
"chunk_overlap": 10,
30+
}
31+
response = client.post("/splitter/ppt", json=request_payload, headers={"api-key": MOCK_API_KEY})
32+
if response.status_code != 200:
33+
print(f"Response status code: {response.status_code}")
34+
print(f"Response content: {response.json()}")
35+
assert response.status_code == 200
36+
assert "chunks" in response.json()
37+
38+
39+
@pytest.mark.asyncio
40+
async def test_split_py():
41+
"""Test splitting Python code into chunks."""
42+
python_code = """
43+
def hello_world():
44+
print("Hello, world!")
45+
"""
46+
python_code_base64 = base64.b64encode(python_code.encode()).decode("utf-8")
47+
request_payload = {"document_content": python_code_base64, "chunk_size": 50, "chunk_overlap": 5}
48+
response = client.post("/splitter/py", json=request_payload, headers={"api-key": MOCK_API_KEY})
49+
assert response.status_code == 200
50+
assert "chunks" in response.json()
51+
52+
53+
@pytest.mark.asyncio
54+
async def test_split_pdf():
55+
"""Test splitting text in a PDF document into chunks."""
56+
pdf_content_base64 = encode_file_to_base64("./tests/test_files/test_document.pdf")
57+
request_payload = {
58+
"document_content": pdf_content_base64,
59+
"chunk_size": 200,
60+
"chunk_overlap": 20,
61+
}
62+
response = client.post("/splitter/pdf", json=request_payload, headers={"api-key": MOCK_API_KEY})
63+
assert response.status_code == 200
64+
assert "chunks" in response.json()
65+
66+
67+
# Define test cases for validate_request()
68+
validate_request_test_cases = [
69+
# Test case 1: valid request
70+
(
71+
SplitterRequest(
72+
document_content="dGVzdA==", chunk_size=100, chunk_overlap=10 # base64 for "test"
73+
),
74+
MOCK_API_KEY,
75+
None,
76+
),
77+
# Test case: invalid API key
78+
(
79+
SplitterRequest(document_content="dGVzdA==", chunk_size=100, chunk_overlap=10),
80+
"invalid_api_key",
81+
HTTPException(status_code=401, detail="Invalid API key"),
82+
),
83+
# Test case 2: missing document content
84+
(
85+
SplitterRequest(document_content="", chunk_size=100, chunk_overlap=10),
86+
MOCK_API_KEY,
87+
HTTPException(status_code=400, detail="No document content provided"),
88+
),
89+
# Test case 4: invalid chunk size
90+
(
91+
SplitterRequest(document_content="dGVzdA==", chunk_size=0, chunk_overlap=10),
92+
MOCK_API_KEY,
93+
HTTPException(status_code=400, detail="No chunk size provided"),
94+
),
95+
# Test case 5: invalid chunk overlap
96+
(
97+
SplitterRequest(document_content="dGVzdA==", chunk_size=100, chunk_overlap=-1),
98+
MOCK_API_KEY,
99+
HTTPException(status_code=400, detail="Chunk overlap must be greater than or equal to 0"),
100+
),
101+
]
102+
103+
104+
@pytest.mark.parametrize("api_request, api_key, expected_exception", validate_request_test_cases)
105+
def test_validate_request(api_request, api_key, expected_exception):
106+
"""Test the validate_request function with various scenarios."""
107+
if expected_exception:
108+
with pytest.raises(HTTPException) as exc_info:
109+
validate_request(api_request, api_key)
110+
assert exc_info.value.status_code == expected_exception.status_code
111+
assert exc_info.value.detail == expected_exception.detail
112+
else:
113+
try:
114+
validate_request(api_request, api_key)
115+
except HTTPException:
116+
pytest.fail("validate_request() raised HTTPException unexpectedly!")

tests/test_files/test_document.pdf

36.2 KB
Binary file not shown.
36.5 KB
Binary file not shown.

0 commit comments

Comments
 (0)