---
estimated_steps: 25
estimated_files: 1
skills_used: []
---

# T02: Write unit tests for compose pipeline logic

Create test_compose_pipeline.py covering compose prompt construction, compose-or-create branching, TechniquePageVideo insertion, and body_sections_format setting.

## Steps

1. Create `backend/pipeline/test_compose_pipeline.py`.

2. Write test fixtures:
   - Mock KeyMoment objects (using simple namedtuples or dataclasses with .title, .summary, .content_type, .start_time, .end_time, .plugins, .raw_transcript, .id, .technique_page_id, .source_video_id)
   - Mock TechniquePage object with .id, .title, .slug, .topic_category, .summary, .body_sections, .signal_chains, .plugins, .source_quality, .creator_id
   - Use `unittest.mock` for DB session and LLM client

3. Test `_build_compose_user_prompt()`:
   - **test_compose_prompt_xml_structure**: verify output contains `<existing_page>`, `<existing_moments>`, `<new_moments>`, `<creator>` tags
   - **test_compose_prompt_offset_indices**: with 3 existing moments and 2 new moments, verify existing use [0]-[2] and new use [3]-[4]
   - **test_compose_prompt_empty_existing_moments**: 0 existing, N new → new moments start at [0]
   - **test_compose_prompt_page_json**: verify existing page serialized as JSON within `<existing_page>` tags

4. Test compose-or-create branching:
   - **test_compose_branch_triggered**: mock session.execute to return an existing page + moments for the same creator+category → verify `_compose_into_existing` is called (patch it)
   - **test_create_branch_no_existing**: mock session.execute to return None for existing page query → verify `_synthesize_chunk` is called instead
   - **test_category_case_insensitive**: verify query uses func.lower for category matching (inspect the query or test with mixed-case input)

5. Test TechniquePageVideo and body_sections_format:
   - **test_body_sections_format_v2**: verify pages created by both compose and create paths have body_sections_format='v2'
   - **test_technique_page_video_inserted**: verify INSERT with on_conflict_do_nothing is executed after page persist

## Must-Haves

- [ ] At least 4 tests for _build_compose_user_prompt covering XML structure, offset math, empty existing, page JSON
- [ ] At least 2 tests for branching logic (compose triggered vs create fallback)
- [ ] At least 1 test for body_sections_format = 'v2'
- [ ] At least 1 test for TechniquePageVideo insertion
- [ ] All tests pass with `python -m pytest backend/pipeline/test_compose_pipeline.py -v`

## Inputs

- ``backend/pipeline/stages.py` — modified by T01 with compose helpers and wiring`
- ``backend/pipeline/test_harness_compose.py` — reference for compose prompt test patterns`
- ``backend/models.py` — TechniquePageVideo model shape for mock construction`

## Expected Output

- ``backend/pipeline/test_compose_pipeline.py` — unit tests for compose prompt construction, branching, TechniquePageVideo, body_sections_format`

## Verification

cd /home/aux/projects/content-to-kb-automator && python -m pytest backend/pipeline/test_compose_pipeline.py -v
