aboutsummaryrefslogtreecommitdiff
path: root/tests/errors/formatting.test.py
blob: 314aff1978296490e5b1ad937d497046cd196e49 (plain)
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
"""Tests for the message formatter helpers in :mod:`edify.errors.formatting`."""

from unittest import mock

from edify.errors.context import CallerContext
from edify.errors.formatting import (
    FixInsertion,
    Problem,
    compose_annotated_message,
    format_error,
    format_fix_block,
    format_help_header,
    format_note_line,
    format_pointer_block,
    format_problem,
    format_problem_header,
)


def _context(source_line: str = "    x = do_the_thing(1, 2, 3)", colno: int = 9) -> CallerContext:
    return CallerContext(
        filename="/tmp/user_code.py",
        lineno=42,
        colno=colno,
        end_colno=colno + 15,
        source_line=source_line,
    )


def test_format_error_with_only_summary_returns_bare_header():
    assert format_error("boom") == "error: boom"


def test_format_error_with_summary_and_blocks_joins_them_with_blank_line():
    output = format_error("boom", "first block", "second block")
    assert output == "error: boom\n\nfirst block\n\nsecond block"


def test_format_error_skips_empty_blocks():
    output = format_error("boom", "", "only real block", "")
    assert output == "error: boom\n\nonly real block"


def test_format_pointer_block_draws_caret_at_caller_column():
    context = _context()
    block = format_pointer_block(context, "here")
    lines = block.splitlines()
    assert lines[0] == "  --> /tmp/user_code.py:42:9"
    assert "^^^^^^^^^^^^^^^ here" in block


def test_format_pointer_block_has_at_least_one_caret_when_span_is_empty():
    context = CallerContext(
        filename="/tmp/x.py",
        lineno=1,
        colno=5,
        end_colno=5,
        source_line="abcde",
    )
    block = format_pointer_block(context, "point")
    assert "^ point" in block


def test_format_note_line_prepends_the_note_prefix():
    assert format_note_line("something is off") == "   = note: something is off"


def test_format_problem_header_prepends_problem_prefix():
    assert format_problem_header("stuff") == "problem: stuff"


def test_format_help_header_prepends_help_prefix():
    assert format_help_header("do X") == "help: do X"


def test_format_fix_block_inserts_text_at_the_target_column():
    context = _context(source_line="value = compute()", colno=1)
    insertion = FixInsertion(column=9, text="new_")
    block = format_fix_block(context, insertion)
    assert "value = new_compute()" in block
    assert "++++" in block


def test_format_problem_composes_header_pointer_help_and_fix():
    problem_context = _context(source_line="pattern = build()", colno=11)
    fix_context = _context(source_line="pattern = build()", colno=11)
    fix_insertion = FixInsertion(column=11, text=".digit()")
    problem = Problem(
        description="missing operand",
        problem_context=problem_context,
        problem_hint="no operand added",
        help_summary="add .digit() before .build()",
        fix_context=fix_context,
        fix_insertion=fix_insertion,
    )
    output = format_problem(problem)
    assert "problem: missing operand" in output
    assert "no operand added" in output
    assert "help: add .digit() before .build()" in output
    assert "++++++++" in output


def test_compose_annotated_message_when_caller_context_is_available():
    output = compose_annotated_message(
        summary="bad thing happened",
        trigger_hint="right here",
        note="because reasons",
        help_line="help: do X",
    )
    assert output.startswith("error: bad thing happened")
    assert "-->" in output
    assert "= note: because reasons" in output
    assert output.rstrip().endswith("help: do X")


def test_compose_annotated_message_falls_back_when_caller_context_is_none():
    with mock.patch("edify.errors.formatting.capture_caller_context", return_value=None):
        output = compose_annotated_message(
            summary="bad thing happened",
            trigger_hint="here",
            note="because reasons",
            help_line="help: do X",
        )
    assert output.startswith("error: bad thing happened")
    assert "-->" not in output
    assert "= note: because reasons" in output
    assert output.rstrip().endswith("help: do X")