diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index f13e13a..106a517 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -9,7 +9,43 @@ on: workflow_dispatch: jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Install system dependencies + shell: sh + run: | + apk add --no-cache git python3 py3-pip gcc musl-dev libpq-dev python3-dev + + - name: Checkout code + shell: sh + run: | + git clone http://gitea.gitea.svc.cluster.local/${{ gitea.repository }}.git . + git checkout ${{ gitea.sha }} + + - name: Install Python dependencies + shell: sh + run: | + pip3 install --break-system-packages -r requirements.txt ruff + + - name: Run ruff linter + shell: sh + run: | + ruff check SPARC/ tests/ + + - name: Run pytest + shell: sh + env: + DATABASE_URL: "sqlite://" + API_KEY: "test-key" + OPENROUTER_API_KEY: "test-key" + JWT_SECRET: "test-secret-for-ci" + APP_ENV: "development" + run: | + python3 -m pytest tests/ -v --tb=short -x + build-api: + needs: test runs-on: ubuntu-latest steps: - name: Install dependencies @@ -81,6 +117,7 @@ jobs: echo "API image available at ${{ steps.tags.outputs.IMAGE_TAG }}" build-frontend: + needs: test runs-on: ubuntu-latest steps: - name: Install dependencies diff --git a/.gitea/workflows/test.yaml b/.gitea/workflows/test.yaml new file mode 100644 index 0000000..9f452fb --- /dev/null +++ b/.gitea/workflows/test.yaml @@ -0,0 +1,46 @@ +name: Test and Lint + +on: + push: + branches: + - main + pull_request: + branches: + - main + workflow_dispatch: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Install system dependencies + shell: sh + run: | + apk add --no-cache git python3 py3-pip gcc musl-dev libpq-dev python3-dev + + - name: Checkout code + shell: sh + run: | + git clone http://gitea.gitea.svc.cluster.local/${{ gitea.repository }}.git . + git checkout ${{ gitea.sha }} + + - name: Install Python dependencies + shell: sh + run: | + pip3 install --break-system-packages -r requirements.txt ruff + + - name: Run ruff linter + shell: sh + run: | + ruff check SPARC/ tests/ + + - name: Run pytest + shell: sh + env: + DATABASE_URL: "sqlite://" + API_KEY: "test-key" + OPENROUTER_API_KEY: "test-key" + JWT_SECRET: "test-secret-for-ci" + APP_ENV: "development" + run: | + python3 -m pytest tests/ -v --tb=short -x diff --git a/SPARC/__init__.py b/SPARC/__init__.py index 9d594cd..687d563 100644 --- a/SPARC/__init__.py +++ b/SPARC/__init__.py @@ -1,3 +1,4 @@ -from .types import Patents, Patent +from .types import Patent as Patent +from .types import Patents as Patents -all = ["Patents", "Patent"] +__all__ = ["Patents", "Patent"] diff --git a/SPARC/analyzer.py b/SPARC/analyzer.py index 2d677cc..996558a 100644 --- a/SPARC/analyzer.py +++ b/SPARC/analyzer.py @@ -13,9 +13,9 @@ from SPARC import config logger = logging.getLogger(__name__) from SPARC.database import DatabaseClient -from SPARC.serp_api import SERP from SPARC.llm import LLMAnalyzer -from SPARC.types import Patent, Patents, CompanyAnalysisResult, BatchAnalysisResult +from SPARC.serp_api import SERP +from SPARC.types import BatchAnalysisResult, CompanyAnalysisResult, Patent, Patents class CompanyAnalyzer: diff --git a/SPARC/database.py b/SPARC/database.py index a22d8e9..4492311 100644 --- a/SPARC/database.py +++ b/SPARC/database.py @@ -1,14 +1,15 @@ """Database client for storing and retrieving LLM messages and user authentication.""" import contextlib -import psycopg2 -from psycopg2.pool import ThreadedConnectionPool -from psycopg2.extras import RealDictCursor -from typing import Dict, List, Optional -from datetime import datetime, timedelta -import json import hashlib +import json +from datetime import datetime, timedelta +from typing import Dict, List, Optional + import bcrypt +import psycopg2 +from psycopg2.extras import RealDictCursor +from psycopg2.pool import ThreadedConnectionPool class DatabaseClient: diff --git a/SPARC/serp_api.py b/SPARC/serp_api.py index b4254d0..cb6a8af 100644 --- a/SPARC/serp_api.py +++ b/SPARC/serp_api.py @@ -1,12 +1,15 @@ import os -import serpapi -from SPARC import config import re -import pdfplumber # pip install pdfplumber -import requests from datetime import datetime, timedelta from typing import Dict -from SPARC.types import Patents, Patent + +import pdfplumber # pip install pdfplumber +import requests +import serpapi + +from SPARC import config +from SPARC.types import Patent, Patents + class SERP: def query(company: str, days_back: int = None) -> Patents: diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 0000000..d3db2f3 --- /dev/null +++ b/ruff.toml @@ -0,0 +1,8 @@ +[lint] +select = ["E", "F", "I"] +ignore = [ + "E501", # line too long (handled by formatter) +] + +[lint.per-file-ignores] +"tests/*" = ["E402", "F841"] # allow import not at top of file, unused vars (mocks) in tests diff --git a/tests/test_analyzer.py b/tests/test_analyzer.py index 4fd6aa3..4977feb 100644 --- a/tests/test_analyzer.py +++ b/tests/test_analyzer.py @@ -1,9 +1,11 @@ """Tests for the high-level company analyzer orchestration.""" +from unittest.mock import MagicMock, Mock + import pytest -from unittest.mock import Mock, patch, call, MagicMock + from SPARC.analyzer import CompanyAnalyzer -from SPARC.types import Patent, Patents, CompanyAnalysisResult, BatchAnalysisResult +from SPARC.types import BatchAnalysisResult, Patent, Patents @pytest.fixture(autouse=True) @@ -24,7 +26,7 @@ class TestCompanyAnalyzer: """Test analyzer initialization with API key.""" mock_llm = mocker.patch("SPARC.analyzer.LLMAnalyzer") - analyzer = CompanyAnalyzer(openrouter_api_key="test-key") + _analyzer = CompanyAnalyzer(openrouter_api_key="test-key") # noqa: F841 mock_llm.assert_called_once_with(api_key="test-key") diff --git a/tests/test_api.py b/tests/test_api.py index a5923c6..169be27 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -1,12 +1,13 @@ """Tests for FastAPI web service endpoints.""" -import pytest from datetime import datetime -from unittest.mock import Mock, patch +from unittest.mock import Mock + +import pytest from fastapi.testclient import TestClient from SPARC.api import app -from SPARC.types import CompanyAnalysisResult, BatchAnalysisResult +from SPARC.types import BatchAnalysisResult, CompanyAnalysisResult @pytest.fixture diff --git a/tests/test_llm.py b/tests/test_llm.py index 154fdac..56bac29 100644 --- a/tests/test_llm.py +++ b/tests/test_llm.py @@ -1,7 +1,9 @@ """Tests for LLM analysis functionality.""" +from unittest.mock import Mock + import pytest -from unittest.mock import Mock, MagicMock, patch + from SPARC.llm import LLMAnalyzer diff --git a/tests/test_serp_api.py b/tests/test_serp_api.py index e6d123d..d6e429b 100644 --- a/tests/test_serp_api.py +++ b/tests/test_serp_api.py @@ -1,9 +1,8 @@ """Tests for SERP API patent retrieval and parsing functionality.""" -import os -import pytest -from unittest.mock import patch, Mock from datetime import datetime, timedelta +from unittest.mock import Mock + from SPARC.serp_api import SERP from SPARC.types import Patent