From 71a037c5ced2109a969846c25365b80c34ec4e0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20S=C3=A1nchez-Gallego=20Kadri?= <126669056+laurasgkadri98@users.noreply.github.com> Date: Wed, 7 Aug 2024 08:37:52 +0200 Subject: [PATCH] testing: add unit testing (#11) Co-authored-by: Roberto Pastor Muela <37798125+RobPasMue@users.noreply.github.com> --- .github/workflows/ci_cd.yml | 39 ++++++++ app/fastapi_utils.py | 6 +- requirements.txt | 3 + setup.py | 7 ++ tests/__init__.py | 1 + tests/conftest.py | 13 +++ tests/test_endpoints_splitter.py | 116 ++++++++++++++++++++++++ tests/test_files/test_document.pdf | Bin 0 -> 37092 bytes tests/test_files/test_presentation.pptx | Bin 0 -> 37378 bytes tests/test_list_functions.py | 58 ++++++++++++ 10 files changed, 240 insertions(+), 3 deletions(-) create mode 100644 setup.py create mode 100644 tests/__init__.py create mode 100644 tests/conftest.py create mode 100644 tests/test_endpoints_splitter.py create mode 100644 tests/test_files/test_document.pdf create mode 100644 tests/test_files/test_presentation.pptx create mode 100644 tests/test_list_functions.py diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index ff6d28a..ab53757 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -26,6 +26,45 @@ jobs: - name: "Run PyAnsys code style checks" uses: ansys/actions/code-style@v6 + tests: + name: "Tests" + runs-on: ${{ matrix.os }} + needs: [code-style] + strategy: + matrix: + os: [ubuntu-latest, windows-latest] + python-version: ['3.9', '3.12'] + fail-fast: false + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + + - name: Testing + uses: ansys/actions/tests-pytest@v6 + timeout-minutes: 12 + with: + checkout: false + skip-install: true + pytest-extra-args: "--cov=ansys.allie.flowkit.python --cov-report=term --cov-report=html:.cov/html --cov-report=xml:.cov/coverage.xml" + + - name: Upload coverage results (HTML) + uses: actions/upload-artifact@v4 + if: (matrix.python-version == env.MAIN_PYTHON_VERSION) && (runner.os == 'Linux') + with: + name: coverage-html + path: .cov/html + retention-days: 7 + release: name: "Release project" if: github.event_name == 'push' && contains(github.ref, 'refs/tags') diff --git a/app/fastapi_utils.py b/app/fastapi_utils.py index b700084..133644e 100644 --- a/app/fastapi_utils.py +++ b/app/fastapi_utils.py @@ -77,8 +77,8 @@ def get_parameters_info(params): if param.annotation == bytes: param_info = ParameterInfo(name=param.name, type="bytes") parameters_info.append(param_info) - elif hasattr(param.annotation, "schema"): - schema = param.annotation.schema() + elif hasattr(param.annotation, "model_json_schema"): + schema = param.annotation.model_json_schema() param_info = extract_fields_from_schema(schema) parameters_info.extend(param_info) else: @@ -101,7 +101,7 @@ def get_return_type_info(return_type: Type[BaseModel]): A list of ParameterInfo objects representing the return type fields. """ - if hasattr(return_type, "schema"): + if hasattr(return_type, "model_json_schema"): schema = return_type.model_json_schema() return extract_fields_from_schema(schema) return [ParameterInfo(name="return", type=str(return_type.__name__))] diff --git a/requirements.txt b/requirements.txt index 105af80..c17fcc8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,9 @@ fastapi==0.112.0 +httpx==0.27.0 langchain==0.2.12 pydantic==2.8.2 pymupdf==1.24.9 +pytest==8.3.2 +pytest-cov==5.0.0 python_pptx==1.0.1 PyYAML==6.0.1 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..0033fb9 --- /dev/null +++ b/setup.py @@ -0,0 +1,7 @@ +from setuptools import find_packages, setup + +setup( + name="ansys-allie-flowkit-python", + version="0.1.0", + packages=find_packages(include=["app", "docker", "configs"]), +) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..f1b390f --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1 @@ +"""Tests module.""" diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..4f9d61d --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,13 @@ +from unittest.mock import patch + +import pytest + +# Mock API key for testing +MOCK_API_KEY = "test_api_key" + + +@pytest.fixture(autouse=True) +def mock_api_key(): + """Mock the API key for testing.""" + with patch("app.config.CONFIG.flowkit_python_api_key", MOCK_API_KEY): + yield diff --git a/tests/test_endpoints_splitter.py b/tests/test_endpoints_splitter.py new file mode 100644 index 0000000..c947fda --- /dev/null +++ b/tests/test_endpoints_splitter.py @@ -0,0 +1,116 @@ +import base64 + +from app.app import app +from app.endpoints.splitter import validate_request +from app.models.splitter import SplitterRequest +from fastapi import HTTPException +from fastapi.testclient import TestClient +import pytest + +from tests.conftest import MOCK_API_KEY + +# Create a test client +client = TestClient(app) + + +def encode_file_to_base64(file_path): + """Encode a file to base64 string.""" + with open(file_path, "rb") as file: + return base64.b64encode(file.read()).decode("utf-8") + + +@pytest.mark.asyncio +async def test_split_ppt(): + """Test splitting text in a PowerPoint document into chunks.""" + ppt_content_base64 = encode_file_to_base64("./tests/test_files/test_presentation.pptx") + request_payload = { + "document_content": ppt_content_base64, + "chunk_size": 100, + "chunk_overlap": 10, + } + response = client.post("/splitter/ppt", json=request_payload, headers={"api-key": MOCK_API_KEY}) + if response.status_code != 200: + print(f"Response status code: {response.status_code}") + print(f"Response content: {response.json()}") + assert response.status_code == 200 + assert "chunks" in response.json() + + +@pytest.mark.asyncio +async def test_split_py(): + """Test splitting Python code into chunks.""" + python_code = """ + def hello_world(): + print("Hello, world!") + """ + python_code_base64 = base64.b64encode(python_code.encode()).decode("utf-8") + request_payload = {"document_content": python_code_base64, "chunk_size": 50, "chunk_overlap": 5} + response = client.post("/splitter/py", json=request_payload, headers={"api-key": MOCK_API_KEY}) + assert response.status_code == 200 + assert "chunks" in response.json() + + +@pytest.mark.asyncio +async def test_split_pdf(): + """Test splitting text in a PDF document into chunks.""" + pdf_content_base64 = encode_file_to_base64("./tests/test_files/test_document.pdf") + request_payload = { + "document_content": pdf_content_base64, + "chunk_size": 200, + "chunk_overlap": 20, + } + response = client.post("/splitter/pdf", json=request_payload, headers={"api-key": MOCK_API_KEY}) + assert response.status_code == 200 + assert "chunks" in response.json() + + +# Define test cases for validate_request() +validate_request_test_cases = [ + # Test case 1: valid request + ( + SplitterRequest( + document_content="dGVzdA==", chunk_size=100, chunk_overlap=10 # base64 for "test" + ), + MOCK_API_KEY, + None, + ), + # Test case: invalid API key + ( + SplitterRequest(document_content="dGVzdA==", chunk_size=100, chunk_overlap=10), + "invalid_api_key", + HTTPException(status_code=401, detail="Invalid API key"), + ), + # Test case 2: missing document content + ( + SplitterRequest(document_content="", chunk_size=100, chunk_overlap=10), + MOCK_API_KEY, + HTTPException(status_code=400, detail="No document content provided"), + ), + # Test case 4: invalid chunk size + ( + SplitterRequest(document_content="dGVzdA==", chunk_size=0, chunk_overlap=10), + MOCK_API_KEY, + HTTPException(status_code=400, detail="No chunk size provided"), + ), + # Test case 5: invalid chunk overlap + ( + SplitterRequest(document_content="dGVzdA==", chunk_size=100, chunk_overlap=-1), + MOCK_API_KEY, + HTTPException(status_code=400, detail="Chunk overlap must be greater than or equal to 0"), + ), +] + + +@pytest.mark.parametrize("api_request, api_key, expected_exception", validate_request_test_cases) +def test_validate_request(api_request, api_key, expected_exception): + """Test the validate_request function with various scenarios.""" + if expected_exception: + with pytest.raises(HTTPException) as exc_info: + validate_request(api_request, api_key) + assert exc_info.value.status_code == expected_exception.status_code + assert exc_info.value.detail == expected_exception.detail + else: + try: + validate_request(api_request, api_key) + except HTTPException: + pytest.fail("validate_request() raised HTTPException unexpectedly!") diff --git a/tests/test_files/test_document.pdf b/tests/test_files/test_document.pdf new file mode 100644 index 0000000000000000000000000000000000000000..4898d606dc8fc4bca72b4bbe1e7e495d75483f2c GIT binary patch literal 37092 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 Jp}g*>{{sXCrf~oO literal 0 HcmV?d00001 diff --git a/tests/test_files/test_presentation.pptx b/tests/test_files/test_presentation.pptx new file mode 100644 index 0000000000000000000000000000000000000000..c6eb5e7503e1d72d20242e098407e048c028c571 GIT binary patch literal 37378 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 literal 0 HcmV?d00001 diff --git a/tests/test_list_functions.py b/tests/test_list_functions.py new file mode 100644 index 0000000..c2e50b4 --- /dev/null +++ b/tests/test_list_functions.py @@ -0,0 +1,58 @@ +from app.app import app +from fastapi.testclient import TestClient +import pytest + +# Initialize the test client +client = TestClient(app) + + +@pytest.mark.asyncio +async def test_list_functions(): + """Test listing available functions.""" + # Test splitter results + response = client.get("/", headers={"api-key": "test_api_key"}) + assert response.status_code == 200 + response_data = response.json() + + expected_response_start = [ + { + "name": "split_ppt", + "path": "/splitter/ppt", + "inputs": [ + {"name": "document_content", "type": "string(binary)"}, + {"name": "chunk_size", "type": "integer"}, + {"name": "chunk_overlap", "type": "integer"}, + ], + "outputs": [{"name": "chunks", "type": "array"}], + "definitions": {}, + }, + { + "name": "split_py", + "path": "/splitter/py", + "inputs": [ + {"name": "document_content", "type": "string(binary)"}, + {"name": "chunk_size", "type": "integer"}, + {"name": "chunk_overlap", "type": "integer"}, + ], + "outputs": [{"name": "chunks", "type": "array"}], + "definitions": {}, + }, + { + "name": "split_pdf", + "path": "/splitter/pdf", + "inputs": [ + {"name": "document_content", "type": "string(binary)"}, + {"name": "chunk_size", "type": "integer"}, + {"name": "chunk_overlap", "type": "integer"}, + ], + "outputs": [{"name": "chunks", "type": "array"}], + "definitions": {}, + }, + ] + + assert response_data[:3] == expected_response_start + + # Test invalid API key + response = client.get("/", headers={"api-key": "invalid_api_key"}) + assert response.status_code == 401 + assert response.json() == {"detail": "Invalid API key"}