"""Shared test fixtures for Resume Matcher backend tests.""" import copy import importlib import pytest # --------------------------------------------------------------------------- # Sample resume data — full ResumeData-compatible dict # --------------------------------------------------------------------------- @pytest.fixture def sample_resume() -> dict: """A realistic resume dict matching the ResumeData schema.""" return { "personalInfo": { "name": "Jane Doe", "title": "Senior Backend Engineer", "email": "jane@example.com", "phone": "+1-555-0100", "location": "San Francisco, CA", "website": "https://janedoe.dev", "linkedin": "linkedin.com/in/janedoe", "github": "github.com/janedoe", }, "summary": "Backend engineer with 6 years of experience building scalable Python APIs and microservices.", "workExperience": [ { "id": 1, "title": "Senior Backend Engineer", "company": "Acme Corp", "location": "San Francisco, CA", "years": "Jan 2021 - Present", "description": [ "Built REST APIs serving 50K requests/day using Python and FastAPI", "Led migration from monolith to microservices architecture", "Mentored 3 junior developers on backend best practices", ], }, { "id": 2, "title": "Software Engineer", "company": "StartupCo", "location": "New York, NY", "years": "Jun 2018 - Dec 2020", "description": [ "Developed payment processing system handling $2M monthly", "Wrote unit and integration tests improving coverage from 40% to 85%", ], }, ], "education": [ { "id": 1, "institution": "MIT", "degree": "B.S. Computer Science", "years": "2014 - 2018", "description": "Graduated with honors, Dean's List", } ], "personalProjects": [ { "id": 1, "name": "OpenAPI Generator", "role": "Creator & Maintainer", "years": "Mar 2021 - Present", "description": [ "CLI tool generating API clients from OpenAPI specs", "500+ GitHub stars, used by 30+ companies", ], } ], "additional": { "technicalSkills": ["Python", "FastAPI", "Docker", "AWS", "PostgreSQL", "Redis"], "languages": ["English (Native)", "Spanish (Conversational)"], "certificationsTraining": ["AWS Solutions Architect Associate"], "awards": ["Employee of the Year 2022"], }, "customSections": {}, "sectionMeta": [], } @pytest.fixture def sample_resume_copy(sample_resume) -> dict: """Deep copy of sample_resume for mutation-safe tests.""" return copy.deepcopy(sample_resume) # --------------------------------------------------------------------------- # Job-related fixtures # --------------------------------------------------------------------------- @pytest.fixture def sample_job_keywords() -> dict: """Extracted job keywords matching the LLM output format.""" return { "required_skills": ["Python", "FastAPI", "Docker", "Kubernetes"], "preferred_skills": ["AWS", "Terraform", "GraphQL"], "experience_requirements": ["5+ years backend development"], "education_requirements": ["Bachelor's in CS or equivalent"], "key_responsibilities": [ "Design and build scalable APIs", "Lead technical architecture decisions", ], "keywords": ["microservices", "CI/CD", "agile", "REST API"], "experience_years": 5, "seniority_level": "senior", } @pytest.fixture def sample_job_description() -> str: """A realistic job description text.""" return ( "Senior Backend Engineer at TechCorp\n\n" "We are looking for a Senior Backend Engineer to join our platform team. " "You will design and build scalable APIs using Python and FastAPI. " "Experience with Docker, Kubernetes, and AWS is required. " "Terraform and GraphQL experience is a plus.\n\n" "Requirements:\n" "- 5+ years backend development experience\n" "- Strong Python skills with FastAPI or similar frameworks\n" "- Experience with microservices architecture\n" "- Familiarity with CI/CD pipelines and agile methodologies\n" "- Bachelor's degree in CS or equivalent\n" ) # --------------------------------------------------------------------------- # Master resume — used for alignment validation # --------------------------------------------------------------------------- @pytest.fixture def master_resume(sample_resume) -> dict: """Master resume (source of truth) — same as sample_resume by default.""" return copy.deepcopy(sample_resume) # --------------------------------------------------------------------------- # ResumeChange fixtures for diff-based tests # --------------------------------------------------------------------------- @pytest.fixture def sample_changes(): """A set of ResumeChange dicts covering all action types.""" from app.schemas.models import ResumeChange return [ ResumeChange( path="summary", action="replace", original="Backend engineer with 6 years of experience building scalable Python APIs and microservices.", value="Senior backend engineer with 6 years building scalable Python APIs, microservices, and cloud infrastructure on AWS.", reason="Added cloud/AWS keywords from JD", ), ResumeChange( path="workExperience[0].description[0]", action="replace", original="Built REST APIs serving 50K requests/day using Python and FastAPI", value="Designed and built REST APIs serving 50K requests/day using Python, FastAPI, and Docker", reason="Added Docker keyword from JD", ), ResumeChange( path="workExperience[0].description", action="append", original=None, value="Implemented CI/CD pipelines with GitHub Actions reducing deploy time by 40%", reason="Added CI/CD keyword from JD", ), ResumeChange( path="additional.technicalSkills", action="reorder", original=None, value=["Python", "FastAPI", "Docker", "AWS", "PostgreSQL", "Redis"], reason="Already in good order, no change needed", ), ] # --------------------------------------------------------------------------- # Isolated database — swap the global TinyDB singleton for a temp-file DB # --------------------------------------------------------------------------- @pytest.fixture async def isolated_db(tmp_path, monkeypatch): """Replace the global ``db`` singleton with a disposable temp-file SQLite DB across ``app.database`` and every router module that imported it. Lets endpoint / e2e tests run against a REAL (but isolated) database instead of a MagicMock, so persistence, the master-resume invariant, and CRUD are actually exercised — without touching the developer's real database. A temp **file** (not ``:memory:``) is required: SQLite's connection pool gives each connection its own in-memory DB, so the async + sync engines would not share state. """ import app.database as database_module from app.database import Database test_db = Database(db_path=tmp_path / "isolated_db.db") monkeypatch.setattr(database_module, "db", test_db) for router_name in ( "resumes", "jobs", "enrichment", "config", "health", "applications", "resume_wizard", ): try: module = importlib.import_module(f"app.routers.{router_name}") except ModuleNotFoundError: continue if hasattr(module, "db"): monkeypatch.setattr(module, "db", test_db) try: yield test_db finally: await test_db.close()