test_table_parser.py
6.17 KB
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
"""Unit tests for table_parser module."""
import pytest
from hermes_feishu.table_parser import (
ParsedTable,
TableColumn,
TableCell,
contains_table,
parse_table,
split_table_and_text,
_infer_column_type,
_parse_row,
)
class TestParseRow:
def test_basic_row(self):
result = _parse_row("| Name | Age |")
assert result == ["Name", "Age"]
def test_row_with_spaces(self):
result = _parse_row("| Hello World | Test |")
assert result == ["Hello World", "Test"]
def test_single_column(self):
result = _parse_row("| Only |")
assert result == ["Only"]
def test_empty_cells(self):
result = _parse_row("| A | | C |")
assert result == ["A", "", "C"]
def test_not_a_row(self):
result = _parse_row("This is not a table row")
assert result == []
def test_cell_with_pipe_in_content(self):
result = _parse_row("| a | b | c | d |")
assert len(result) == 4
def test_cell_with_special_chars(self):
result = _parse_row("| $100 | 50% | 1,000 |")
assert result == ["$100", "50%", "1,000"]
class TestInferColumnType:
def test_all_numbers(self):
values = ["1", "2", "3", "10.5"]
assert _infer_column_type(values) == "number"
def test_mixed(self):
values = ["1", "hello", "3"]
assert _infer_column_type(values) == "text"
def test_all_text(self):
values = ["apple", "banana", "cherry"]
assert _infer_column_type(values) == "text"
def test_empty(self):
assert _infer_column_type([]) == "text"
def test_numbers_with_commas(self):
values = ["1,000", "2,500", "10,000"]
assert _infer_column_type(values) == "number"
def test_numbers_with_percent(self):
values = ["50%", "75%", "100%"]
assert _infer_column_type(values) == "number"
def test_all_empty_strings(self):
assert _infer_column_type(["", "", ""]) == "text"
class TestContainsTable:
def test_has_table(self):
md = "| Name | Age |\n| --- | --- |\n| Alice | 30 |"
assert contains_table(md) is True
def test_no_table(self):
md = "Just some text without a table."
assert contains_table(md) is False
def test_table_in_text(self):
md = "Here is some data:\n| A | B |\n| --- | --- |\n| 1 | 2 |\n\nDone."
assert contains_table(md) is True
def test_pipe_in_code_block(self):
md = "```\n| this | is | code |\n```\n| real | table |\n| --- | --- |"
# The code block pipes don't form a valid table (no separator row),
# but the real table after the code block should be detected.
assert contains_table(md) is True
class TestParseTable:
def test_basic_table(self):
md = "| Name | Age |\n| --- | --- |\n| Alice | 30 |\n| Bob | 25 |"
tables = parse_table(md)
assert len(tables) == 1
table = tables[0]
assert len(table.headers) == 2
assert table.headers[0].name == "Name"
assert table.headers[1].name == "Age"
assert len(table.rows) == 2
assert table.rows[0][0].text == "Alice"
assert table.rows[0][1].text == "30"
def test_table_with_alignments(self):
md = "| Left | Center | Right |\n| :--- | :---: | ---: |\n| A | B | C |"
tables = parse_table(md)
assert len(tables) == 1
assert len(tables[0].headers) == 3
assert len(tables[0].rows) == 1
def test_single_row_table(self):
md = "| H1 | H2 |\n| --- | --- |"
tables = parse_table(md)
assert len(tables) == 1
assert len(tables[0].headers) == 2
assert len(tables[0].rows) == 0
def test_multiple_tables(self):
md = "| A | B |\n| --- | --- |\n| 1 | 2 |\n\n| X | Y |\n| --- | --- |\n| a | b |"
tables = parse_table(md)
assert len(tables) == 2
def test_column_type_inference(self):
md = "| Name | Score | Count |\n| --- | --- | --- |\n| Alice | 95.5 | 100 |\n| Bob | 87 | 200 |"
tables = parse_table(md)
table = tables[0]
assert table.headers[0].field_type == "text"
assert table.headers[1].field_type == "number"
assert table.headers[2].field_type == "number"
def test_table_with_empty_cells(self):
md = "| A | B |\n| --- | --- |\n| hello | |\n| | world |"
tables = parse_table(md)
table = tables[0]
assert table.rows[0][1].text == ""
assert table.rows[1][0].text == ""
def test_table_with_markdown_in_cells(self):
md = "| Feature | Status |\n| --- | --- |\n| **Bold** | `code` |\n| *Italic* | [link](url) |"
tables = parse_table(md)
table = tables[0]
assert table.rows[0][0].text == "**Bold**"
assert table.rows[1][0].text == "*Italic*"
def test_no_table_returns_empty(self):
tables = parse_table("No tables here")
assert tables == []
def test_preserves_raw_markdown(self):
md = "| A | B |\n| --- | --- |\n| 1 | 2 |"
tables = parse_table(md)
assert tables[0].raw_markdown == md
class TestSplitTableAndText:
def test_text_before_table(self):
md = "Here is the data:\n| A | B |\n| --- | --- |\n| 1 | 2 |"
table_blocks, text_segments = split_table_and_text(md)
assert len(table_blocks) == 1
assert len(text_segments) == 1
assert "Here is the data" in text_segments[0]
def test_table_surrounded_by_text(self):
md = "Intro\n| A | B |\n| --- | --- |\n| 1 | 2 |\nOutro"
table_blocks, text_segments = split_table_and_text(md)
assert len(table_blocks) == 1
assert len(text_segments) == 2
assert "Intro" in text_segments[0]
assert "Outro" in text_segments[1]
def test_no_table(self):
md = "Just text here"
table_blocks, text_segments = split_table_and_text(md)
assert len(table_blocks) == 0
assert len(text_segments) == 1
def test_multiple_tables_with_text(self):
md = "First:\n| A | B |\n| --- | --- |\n| 1 | 2 |\n\nSecond:\n| X | Y |\n| --- | --- |\n| a | b |\nEnd"
table_blocks, text_segments = split_table_and_text(md)
assert len(table_blocks) == 2
assert len(text_segments) == 3