File size: 3,902 Bytes
c1f3d83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""present(): the boundary where a bin/ tool's plain output gets the house style.

The tools print flush-left ASCII so they stay greppable and pipe-friendly when
run directly; presentation belongs to the CLI. That split is only safe if the
boundary is exact, which is what these assert.

Colour constants are empty unless stdout is a terminal, so each test that cares
about colour substitutes sentinels for them.
"""
import pytest

import nexa


@pytest.fixture()
def tint(monkeypatch):
    """Sentinel colours, so a test can assert which one was chosen."""
    for name, value in (("GREEN", "<G>"), ("RED", "<R>"), ("AMBER", "<A>"),
                        ("GREY", "<g>"), ("WHITE", "<W>"), ("DIM", "<D>"),
                        ("RESET", "<0>")):
        monkeypatch.setattr(nexa, name, value)
    monkeypatch.setattr(nexa, "HBAR", "-")
    monkeypatch.setattr(nexa, "TICK", "+")
    monkeypatch.setattr(nexa, "CROSS", "x")
    monkeypatch.setattr(nexa, "BANG", "!")


def test_blank_lines_collapse_to_empty():
    assert nexa.present("") == ""
    assert nexa.present("   \t ") == ""


def test_body_is_indented_under_the_banner():
    assert nexa.present("32 rules").startswith("   ")


def test_relative_indentation_is_preserved():
    """A tool's own nesting still has to read as nesting after the shift."""
    outer = nexa.present("Decided by an AST pass:")
    inner = nexa.present("  [critical] odoo.prefer-sql-constraint")
    assert len(inner) - len(inner.lstrip()) > len(outer) - len(outer.lstrip())


def test_a_line_that_already_has_escapes_is_untouched():
    """Otherwise the CLI would restyle its own already-styled output."""
    line = "\033[38;5;51mstyled by the tool itself\033[0m"
    assert nexa.present(line) == line


def test_ascii_separator_becomes_the_house_rule(tint):
    out = nexa.present("----------------------------------------")
    assert out == "   <D>" + "-" * 66 + "<0>"


def test_short_dashes_are_not_a_separator(tint):
    """A hyphenated word is not a horizontal rule."""
    assert "-" * 66 not in nexa.present("a - b")


@pytest.mark.parametrize("line,expected", [
    ("PASS: 32 rules valid", "<G>"),
    ("OK everything", "<G>"),
    ("FAIL: 2 problems", "<R>"),
    ("ERROR: broken", "<R>"),
    ("WARN: something", "<A>"),
    ("Rules retrieved:", "<A>"),
    ("Findings", "<A>"),
    ("  0.586   [high] odoo.prefer-sql-constraint", "<g>"),
])
def test_colour_selection(tint, line, expected):
    assert nexa.present(line).startswith("   " + expected)


def test_severity_tags_are_tinted_inside_a_grey_line(tint):
    out = nexa.present("  [critical] odoo.prefer-sql-constraint")
    assert "<R>[critical]<0>" in out


# ------------------------------------------------------------- code fences

def test_fence_markers_are_dimmed(tint):
    assert nexa.present("```python").startswith("   <D>")


def test_code_inside_a_fence_is_white(tint):
    assert nexa.present("    rec.qty = 1", in_code=True).startswith("   <W>")


def test_a_def_inside_a_fence_is_not_a_heading(tint):
    """It ends with a colon at the margin, which is the prose heading rule.
    Inside a fence that rule must not apply, or every function signature in
    every answer turns amber."""
    assert nexa.present("def _check_quantity(self):", in_code=True).startswith("   <W>")


def test_a_def_outside_a_fence_is_not_treated_as_a_heading_either(tint):
    """The heading rule requires no colon anywhere, or a trailing colon on a
    short line; a signature has parentheses and is long."""
    out = nexa.present("def _check_quantity(self):")
    assert out.startswith("   <A>") or out.startswith("   <g>")


def test_severity_tags_are_left_alone_inside_code(tint):
    """Code is code. Rewriting a bracketed word inside a fenced block would
    corrupt the answer being displayed."""
    out = nexa.present("x = data['[critical]']", in_code=True)
    assert "<R>" not in out