---
estimated_steps: 42
estimated_files: 3
skills_used: []
---

# T05: Integration tests for pipeline, embedding, Qdrant, and trigger endpoints

Write comprehensive integration tests with mocked LLM and Qdrant that verify the entire pipeline flow. Tests use the existing pytest-asyncio infrastructure from S02, extended with mocks for the OpenAI client and Qdrant client.

## Negative Tests

- **Malformed inputs**: LLM returns invalid JSON → pipeline handles gracefully, logs error
- **Error paths**: LLM primary endpoint connection refused → falls back to secondary
- **Boundary conditions**: Video with 0 segments → pipeline completes without errors, no moments created

## Steps

1. Create `backend/tests/fixtures/mock_llm_responses.py`:
   - Dictionary mapping stage names to valid JSON response strings matching each Pydantic schema
   - Stage 2: SegmentationResult with 2 topic segments covering the 5 sample transcript segments
   - Stage 3: ExtractionResult with 2 key moments (one technique, one settings)
   - Stage 4: ClassificationResult mapping moments to canonical tags
   - Stage 5: SynthesisResult with a technique page including body_sections, signal_chains, plugins
   - Embedding response: list of 768-dimensional vectors (can be random floats for testing)

2. Create `backend/tests/test_pipeline.py` with these tests using `unittest.mock.patch` on `openai.OpenAI` and `qdrant_client.QdrantClient`:
   a. `test_stage2_segmentation_updates_topic_labels`: Ingest sample transcript, run stage2 with mocked LLM, verify TranscriptSegment rows have topic_label set
   b. `test_stage3_extraction_creates_key_moments`: Run stages 2+3, verify KeyMoment rows created with correct fields, processing_status = extracted
   c. `test_stage4_classification_assigns_tags`: Run stages 2+3+4, verify moments have topic_category and topic_tags from canonical list
   d. `test_stage5_synthesis_creates_technique_pages`: Run full pipeline stages 2-5, verify TechniquePage rows created with body_sections, signal_chains, summary. Verify KeyMoments linked to TechniquePage via technique_page_id
   e. `test_stage6_embeds_and_upserts_to_qdrant`: Run full pipeline, verify EmbeddingClient.embed called with correct texts, QdrantManager.upsert called with correct payloads
   f. `test_run_pipeline_resumes_from_extracted`: Set video processing_status to extracted, run pipeline, verify only stages 4+5+6 execute (not 2+3)
   g. `test_pipeline_trigger_endpoint`: POST to /api/v1/pipeline/trigger/{video_id} with mocked Celery delay, verify 200 response
   h. `test_pipeline_trigger_404_for_missing_video`: POST to /api/v1/pipeline/trigger/{nonexistent_id}, verify 404
   i. `test_ingest_dispatches_pipeline`: POST transcript to ingest endpoint with mocked run_pipeline.delay, verify delay was called with video_id
   j. `test_llm_fallback_on_primary_failure`: Mock primary endpoint to raise APIConnectionError, verify fallback endpoint is called

   IMPORTANT: Pipeline stages use SYNC SQLAlchemy (not async). Tests that call stage functions directly need a real PostgreSQL test database. Use the existing conftest.py pattern but create sync engine/sessions for pipeline stage calls. Mock the LLM and Qdrant clients, not the DB.

3. Update `backend/tests/conftest.py` to add:
   - A fixture that creates a sync SQLAlchemy engine pointing to the test database (for pipeline stage tests)
   - A fixture that pre-ingests a sample transcript and returns the video_id (for pipeline tests to use)
   - Any necessary mock fixtures for LLMClient and QdrantManager

## Must-Haves

- [ ] 10 integration tests covering all pipeline stages, trigger endpoints, resumability, and fallback
- [ ] All LLM calls mocked with realistic response fixtures
- [ ] Qdrant calls mocked — no real Qdrant needed
- [ ] Pipeline stage tests use sync DB sessions (matching production Celery behavior)
- [ ] At least one negative test (LLM fallback on primary failure)
- [ ] All tests pass with `python -m pytest tests/test_pipeline.py -v`

## Verification

- `cd backend && python -m pytest tests/test_pipeline.py -v` — all tests pass
- `cd backend && python -m pytest tests/ -v` — all tests (including S02 ingest tests) still pass

## Observability Impact

- Signals added: test output shows per-test pass/fail with timing, mock call assertions verify logging would fire in production
- How a future agent inspects this: run `pytest tests/test_pipeline.py -v` to see all pipeline test results

## Inputs

- ``backend/pipeline/stages.py` — all pipeline stages from T02+T03`
- ``backend/pipeline/llm_client.py` — LLMClient to mock from T01`
- ``backend/pipeline/embedding_client.py` — EmbeddingClient to mock from T03`
- ``backend/pipeline/qdrant_client.py` — QdrantManager to mock from T03`
- ``backend/pipeline/schemas.py` — Pydantic models for response fixtures from T01`
- ``backend/routers/pipeline.py` — trigger endpoint from T04`
- ``backend/routers/ingest.py` — updated ingest with pipeline dispatch from T04`
- ``backend/tests/conftest.py` — existing test infrastructure from S02`
- ``backend/tests/fixtures/sample_transcript.json` — sample data from S02`
- ``backend/models.py` — ORM models for DB verification`

## Expected Output

- ``backend/tests/fixtures/mock_llm_responses.py` — mock LLM response fixtures for all stages`
- ``backend/tests/test_pipeline.py` — 10 integration tests for full pipeline flow`
- ``backend/tests/conftest.py` — extended with sync engine fixture and pre-ingest fixture`

## Verification

cd backend && python -m pytest tests/test_pipeline.py -v && python -m pytest tests/ -v
