diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..d2bdd65 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,30 @@ +name: CI Pipeline + +on: [push, pull_request] + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python 3.9 + uses: actions/setup-python@v2 + with: + python-version: 3.9 + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + pip install pytest pytest-cov + + - name: Run tests + env: + DATABASE_URL: sqlite:///test.db + run: | + pytest --cov=./ --cov-report=xml + + - name: Upload coverage + uses: codecov/codecov-action@v1 \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7f4de12 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,30 @@ +# Use official Python image +FROM python:3.9-slim + +# Set environment variables +ENV PYTHONDONTWRITEBYTECODE 1 +ENV PYTHONUNBUFFERED 1 +ENV DATABASE_URL=sqlite:///./resume_analyzer.db + +# Set work directory +WORKDIR /app + +# Install system dependencies +RUN apt-get update && apt-get install -y \ + gcc \ + python3-dev \ + && rm -rf /var/lib/apt/lists/* + +# Install Python dependencies +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Copy project +COPY . . + +# Create required directories +RUN mkdir -p /app/results \ + && touch /app/resume_analyzer.db + +# Run the application +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] \ No newline at end of file diff --git a/app/services/parser.py b/app/services/parser.py index 0888eb3..9b16554 100644 --- a/app/services/parser.py +++ b/app/services/parser.py @@ -1,6 +1,6 @@ from io import BytesIO import docx -from PyPDF2 import PdfReader +from pypdf import PdfReader class FileParser: @staticmethod diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..21ede49 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,12 @@ +version: '3.8' + +services: + web: + build: . + ports: + - "8000:8000" + volumes: + - .:/app + - ./results:/app/results + environment: + - DATABASE_URL=sqlite:////app/resume_analyzer.db \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..59fa2f1 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,10 @@ +fastapi==0.68.1 +uvicorn==0.15.0 +python-dotenv==0.19.0 +python-multipart==0.0.5 +python-docx==0.8.11 +pypdf==3.17.0 # Changed from PyPDF2 +sqlalchemy==1.4.23 +databases[sqlite]==0.5.5 +pytest==6.2.5 +httpx==0.19.0 \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py index dc8c09e..e9ff422 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,37 +1,40 @@ -import sys +# tests/conftest.py import os - -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) +import sys import pytest from fastapi.testclient import TestClient from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker +# Ensure the 'app' module can be imported +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) + from app.main import app, get_db from app.db.database import Base -from app.db.database import SessionLocal -TEST_DB_URL = "sqlite:///./test.db" +# Use in-memory SQLite DB for fast testing +TEST_DB_URL = "sqlite:///:memory:" -engine = create_engine( - TEST_DB_URL, connect_args={"check_same_thread": False} -) +# Set up the test database engine and session +engine = create_engine(TEST_DB_URL, connect_args={"check_same_thread": False}) TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) -@pytest.fixture() +# Fixture to set up and tear down the database schema +@pytest.fixture(scope="session") def test_db(): Base.metadata.create_all(bind=engine) yield Base.metadata.drop_all(bind=engine) +# Fixture to override the FastAPI dependency and provide a test client @pytest.fixture() def client(test_db): def override_get_db(): + db = TestingSessionLocal() try: - db = TestingSessionLocal() yield db finally: db.close() - + app.dependency_overrides[get_db] = override_get_db - yield TestClient(app) \ No newline at end of file + yield TestClient(app) diff --git a/tests/test_api.py b/tests/test_api.py index 5a06eeb..f813351 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -1,19 +1,26 @@ +# tests/test_api.py import pytest -def test_upload_resume(client): - test_file = ("test_resume.pdf", open("tests/test_resume.pdf", "rb")) - - response = client.post( - "/upload", - files={"file": test_file} - ) - +def test_upload_and_get_result(client): + # Use context manager to ensure the file is closed after the test + test_file_path = "tests/test_resume.pdf" + + with open(test_file_path, "rb") as f: + response = client.post( + "/upload", + files={"file": ("test_resume.pdf", f, "application/pdf")}, + ) + + # Assert upload was successful assert response.status_code == 200 - assert "id" in response.json() - - analysis_id = response.json()["id"] - - # Test getting result + data = response.json() + assert "id" in data + + analysis_id = data["id"] + + # Now test retrieving the analysis result response = client.get(f"/result/{analysis_id}") assert response.status_code == 200 - assert response.json()["status"] in ["processing", "completed"] \ No newline at end of file + + result_data = response.json() + assert result_data["status"] in ["processing", "completed"]