Skip to content

[Feature] Unit tests for intake extractor customer-id and section normalization #11

Description

@sjohnston1972

Part of the "Add an automated backend test suite" parent issue. Depends on the pytest-scaffolding sub-issue.

Background

backend/app/intake/extractor.py turns raw brief text into a structured {customer_id, customer_name, sections, overview, notes} dict. It has a pure regex path (extract_customer_id_regex) and an LLM path (extract_brief) that normalizes the model's JSON (fills all canonical sections, strips/caps the customer id, falls back to the regex).

Problem / Goal

Add unit tests for the extractor's deterministic behavior, stubbing the LLM router so tests stay offline.

Where to look

  • backend/app/intake/extractor.py:35-46_CUSTOMER_RE, extract_customer_id_regex.
  • extractor.py:66-128extract_brief: short-circuit for tiny input (< 20 chars), JSON-parse fallback, section normalization (CANONICAL_SECTIONS = ["LAN","WAN","EDGE","CLOUD","QOS"]), customer-id fallback to regex, 64-char cap.
  • extractor.py:92-98 — it calls router.stream(...); monkeypatch this to yield canned output.

Suggested approach

Write backend/tests/test_extractor.py:

  1. extract_customer_id_regex — matches Customer: ACME, customer - foo, is case-insensitive/multiline, strips trailing punctuation, returns None when absent.
  2. extract_brief short-circuit: input under 20 chars returns the empty-sections stub with the "too short" note, and never calls the router.
  3. extract_brief normalization: monkeypatch router.stream to yield a fenced JSON with only some sections; assert all five canonical keys are present and stripped.
  4. Customer-id fallback: model returns customer_id: null but the text has a Customer: line → regex value is used; assert the 64-char cap.
  5. Router returns unparseable text → the best-effort fallback (regex id, whole text in overview, failure note) is returned.

Use monkeypatch to replace router.stream with an async generator; mark tests async (scaffolding sub-issue enables pytest-asyncio).

Acceptance criteria

  • test_extractor.py covers the five cases above and passes offline.
  • No real LLM call is made (router is stubbed).

Testing

cd backend && pytest tests/test_extractor.py -q.

Out of scope

  • The intake API endpoints and background tasks (api/intake.py) — logic-level extractor tests only here.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions