File size: 2,816 Bytes
c893230
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
"""Tests for messy-notes routing into per-section RICS buckets.

These tests pin the deterministic keyword router (no LLM). The router has
to be reliable offline because the front-end calls it on every upload, and
the LLM refinement pass is opt-in.
"""

from __future__ import annotations

import pytest

from app.generator.notes_router import route_notes


@pytest.mark.asyncio
async def test_route_explicit_section_code_wins() -> None:
    lines = ["E2: tiles slipped on north pitch"]
    out = await route_notes(lines, survey_level=3)
    assert out.bullets_by_section.get("E2") == ["E2: tiles slipped on north pitch"]


@pytest.mark.asyncio
async def test_route_roof_keywords_to_E2() -> None:
    lines = ["several roof tiles slipped at the ridge"]
    out = await route_notes(lines, survey_level=3)
    assert "E2" in out.bullets_by_section
    assert any("tiles slipped" in b for b in out.bullets_by_section["E2"])


@pytest.mark.asyncio
async def test_route_boiler_to_G2() -> None:
    lines = ["boiler 18 years old, no recent service certificate"]
    out = await route_notes(lines, survey_level=3)
    assert "G2" in out.bullets_by_section


@pytest.mark.asyncio
async def test_route_damp_to_F9() -> None:
    lines = ["rising damp visible at rear hallway skirting"]
    out = await route_notes(lines, survey_level=3)
    # damp is the F9 primary signal; skirting also touches F7 — confirm damp wins
    assert "F9" in out.bullets_by_section


@pytest.mark.asyncio
async def test_route_unmatched_line_returned_in_unrouted() -> None:
    lines = ["[surveyor signature on file]"]
    out = await route_notes(lines, survey_level=3)
    # Generic chatter that doesn't map cleanly to any section
    assert out.unrouted_lines == lines or "__unrouted__" not in out.bullets_by_section


@pytest.mark.asyncio
async def test_route_mixed_dump_splits_across_sections() -> None:
    lines = [
        "tile slipped on rear pitch",
        "gutters overflowing at front elevation",
        "consumer unit pre-2008, no RCD",
        "rising damp at rear hallway",
    ]
    out = await route_notes(lines, survey_level=3)
    codes = set(out.bullets_by_section.keys())
    # Each of these should land in a distinct section
    assert "E2" in codes  # roof tile
    assert "E3" in codes  # gutters
    assert "G1" in codes  # consumer unit / RCD
    assert "F9" in codes  # damp


@pytest.mark.asyncio
async def test_route_respects_survey_level() -> None:
    lines = ["roof tiles slipped"]
    out_l1 = await route_notes(lines, survey_level=1)
    out_l3 = await route_notes(lines, survey_level=3)
    # Both packs have an E2-like roof section; the routing should succeed for both.
    assert any(k.startswith("E") for k in out_l1.bullets_by_section)
    assert any(k.startswith("E") for k in out_l3.bullets_by_section)