From b32eebff8ad93fee00f6873cd7c5f1ff223d5386 Mon Sep 17 00:00:00 2001 From: agent-company Date: Sun, 19 Apr 2026 20:06:10 +0000 Subject: [PATCH] ci: enable ruff linting and pytest in CI pipeline Uncomment the ruff check and pytest steps in the Gitea Actions build workflow so that linting violations and test failures block image builds. Fix all pre-existing ruff violations (E402 import ordering in analyzer.py, F821 undefined name in api.py, I001 unsorted imports in test files, F401 unused import in test_rate_limit.py). Closes leeworks-agents/SPARC#1559 Closes leeworks-agents/SPARC#1560 Co-Authored-By: Claude Opus 4.6 (1M context) --- .gitea/workflows/build.yaml | 29 +++++++++++++++-------------- SPARC/analyzer.py | 4 ++-- SPARC/api.py | 8 ++++++-- tests/test_auth.py | 1 + tests/test_rate_limit.py | 3 ++- tests/test_security.py | 7 +++++++ 6 files changed, 33 insertions(+), 19 deletions(-) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index 80acc27..aeeb5c1 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -28,10 +28,10 @@ jobs: run: | pip3 install -r requirements.txt ruff -# - name: Run ruff linter -# shell: sh -# run: | -# ruff check SPARC/ tests/ + - name: Run ruff linter + shell: sh + run: | + ruff check SPARC/ tests/ - name: Install Node.js and check TypeScript types shell: sh @@ -47,16 +47,17 @@ jobs: fi npx tsc --noEmit -# - 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 + - 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: | + pip3 install pytest + python3 -m pytest tests/ -v --tb=short -x build-api: needs: test diff --git a/SPARC/analyzer.py b/SPARC/analyzer.py index 31ad7f1..1ebceaf 100644 --- a/SPARC/analyzer.py +++ b/SPARC/analyzer.py @@ -10,13 +10,13 @@ from concurrent.futures import ThreadPoolExecutor, as_completed from typing import Callable from SPARC import config - -logger = logging.getLogger(__name__) from SPARC.database import DatabaseClient from SPARC.llm import LLMAnalyzer from SPARC.serp_api import SERP from SPARC.types import BatchAnalysisResult, CompanyAnalysisResult, Patent, Patents +logger = logging.getLogger(__name__) + class CompanyAnalyzer: """Orchestrates end-to-end company performance analysis via patents.""" diff --git a/SPARC/api.py b/SPARC/api.py index 3a28033..a42ddd7 100644 --- a/SPARC/api.py +++ b/SPARC/api.py @@ -3,9 +3,14 @@ Provides REST API endpoints for analyzing company patent portfolios. """ +from __future__ import annotations + from contextlib import asynccontextmanager from datetime import datetime -from typing import Annotated, List +from typing import TYPE_CHECKING, Annotated, List + +if TYPE_CHECKING: + from SPARC.database import DatabaseClient from fastapi import BackgroundTasks, Depends, FastAPI, HTTPException, Query, Request from fastapi.middleware.cors import CORSMiddleware @@ -653,7 +658,6 @@ async def export_company_pdf( PDF file download """ import io - import textwrap from reportlab.lib import colors from reportlab.lib.pagesizes import letter diff --git a/tests/test_auth.py b/tests/test_auth.py index de79259..bb4378a 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -175,6 +175,7 @@ class TestGetMe: from datetime import timedelta import jwt as pyjwt + from SPARC.auth import JWT_ALGORITHM, JWT_SECRET payload = { diff --git a/tests/test_rate_limit.py b/tests/test_rate_limit.py index f9f06af..8d0adf0 100644 --- a/tests/test_rate_limit.py +++ b/tests/test_rate_limit.py @@ -1,7 +1,8 @@ """Tests for rate limiting on auth endpoints.""" +from unittest.mock import MagicMock, patch + import pytest -from unittest.mock import Mock, patch, MagicMock from fastapi.testclient import TestClient from SPARC.api import app diff --git a/tests/test_security.py b/tests/test_security.py index b6e4be1..b34deec 100644 --- a/tests/test_security.py +++ b/tests/test_security.py @@ -14,6 +14,7 @@ class TestJWTSecretStartupCheck: with patch.dict(os.environ, {"APP_ENV": "production"}): # Reload config to pick up the new APP_ENV import importlib + import SPARC.config importlib.reload(SPARC.config) @@ -31,6 +32,7 @@ class TestJWTSecretStartupCheck: """Starting with default secret and APP_ENV=development must not raise.""" with patch.dict(os.environ, {"APP_ENV": "development"}): import importlib + import SPARC.config importlib.reload(SPARC.config) @@ -46,6 +48,7 @@ class TestJWTSecretStartupCheck: """Starting with a custom secret in production must not raise.""" with patch.dict(os.environ, {"APP_ENV": "production"}): import importlib + import SPARC.config importlib.reload(SPARC.config) @@ -65,6 +68,7 @@ class TestJWTSecretStartupCheck: env.pop("APP_ENV", None) with patch.dict(os.environ, env, clear=True): import importlib + import SPARC.config importlib.reload(SPARC.config) @@ -84,6 +88,7 @@ class TestCORSConfig: """When CORS_ORIGINS is unset, defaults to localhost origins.""" with patch.dict(os.environ, {"CORS_ORIGINS": ""}): import importlib + import SPARC.config importlib.reload(SPARC.config) assert SPARC.config.cors_origins == [ @@ -95,6 +100,7 @@ class TestCORSConfig: """Setting CORS_ORIGINS configures allowed origins.""" with patch.dict(os.environ, {"CORS_ORIGINS": "https://sparc.example.com,https://app.example.com"}): import importlib + import SPARC.config importlib.reload(SPARC.config) assert SPARC.config.cors_origins == [ @@ -109,6 +115,7 @@ class TestCORSConfig: """A single origin without comma works correctly.""" with patch.dict(os.environ, {"CORS_ORIGINS": "https://sparc.example.com"}): import importlib + import SPARC.config importlib.reload(SPARC.config) assert SPARC.config.cors_origins == ["https://sparc.example.com"]