Skip to content

Commit 80e4b62

Browse files
committed
テスト要件追加
1 parent 886b567 commit 80e4b62

5 files changed

Lines changed: 114 additions & 0 deletions

File tree

docs/agents/TEST_REQUIREMENTS.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ ExStruct のテストは以下のカテゴリに分類される:
4141
- [CEL-05] Unicode(絵文字、日本語、異体字)セルの読み取り
4242
- [CEL-06] Pandas 読み込みによる dtype=string 強制が守られている
4343
- [CEL-07] セル範囲が大きいファイルでも 1 万セル程度で性能問題がない
44+
- [CEL-08] `_coerce_numeric_preserve_format` が整数・小数・非数値を正しく判定する
45+
- [CEL-09] `detect_tables_openpyxl` が openpyxl の Table オブジェクトを検出できる
4446

4547
---
4648

@@ -205,3 +207,6 @@ pydantic 構造が必ず仕様どおりであることを検証する。
205207
- [MODE-05] `process_excel` でモード指定が伝搬し、PDF/画像オプション併用でも正常終了する
206208
- [MODE-06] `standard` モードで既存フィクスチャの出力に回帰がない(不要な図形が増えない)
207209
- [MODE-07] 無効なモード値は処理開始前にエラーとなる
210+
- [INT-01] COM オープン失敗時に `extract_workbook` がセル+テーブル候補のみを返すフォールバックを行う
211+
- [IO-05] `dict_without_empty_values` が None/空リスト/空辞書/空文字列を除去しネスト構造を保持する
212+
- [RENDER-01] Excel+COM+pypdfium2 環境で PDF/PNG を出力できるスモークテスト(環境変数でオンオフ可能)

pyproject.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,10 @@ Homepage = "https://harumiweb.github.io/exstruct/"
4444
Repository = "https://github.com/harumiWeb/exstruct"
4545
Issues = "https://github.com/harumiWeb/exstruct/issues"
4646
Documentation = "https://harumiweb.github.io/exstruct/"
47+
48+
[tool.coverage.run]
49+
omit = [
50+
"tests/*",
51+
"*/test_*.py",
52+
"*/gen_py/*",
53+
]

tests/test_cells_utils.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
from pathlib import Path
2+
3+
import pytest
4+
from openpyxl import Workbook, load_workbook
5+
from openpyxl.worksheet.table import Table, TableStyleInfo
6+
7+
from exstruct.core import cells
8+
from exstruct.core.cells import _coerce_numeric_preserve_format, detect_tables_openpyxl
9+
10+
11+
def test_coerce_numeric_preserve_format() -> None:
12+
assert _coerce_numeric_preserve_format("42") == 42
13+
assert _coerce_numeric_preserve_format("-3.14") == -3.14
14+
# non-numeric stays string
15+
assert _coerce_numeric_preserve_format("3.14e2") == "3.14e2"
16+
assert _coerce_numeric_preserve_format("abc") == "abc"
17+
18+
19+
def test_detect_tables_openpyxl_detects_tables(tmp_path: Path) -> None:
20+
path = tmp_path / "sample.xlsx"
21+
wb = Workbook()
22+
ws = wb.active
23+
ws.title = "Sheet1"
24+
ws.append(["A", "B"])
25+
ws.append([1, 2])
26+
tab = Table(displayName="Table1", ref="A1:B2")
27+
tab.tableStyleInfo = TableStyleInfo(name="TableStyleMedium9", showRowStripes=True)
28+
ws.add_table(tab)
29+
wb.save(path)
30+
wb.close()
31+
32+
tables = detect_tables_openpyxl(path, "Sheet1")
33+
assert "A1:B2" in tables
34+
35+
36+
def test_detect_tables_openpyxl_respects_table_params(tmp_path: Path, monkeypatch) -> None:
37+
# Ensure detection still runs after modifying global thresholds
38+
path = tmp_path / "sample.xlsx"
39+
wb = Workbook()
40+
ws = wb.active
41+
ws.title = "Sheet1"
42+
ws.append(["A", "B"])
43+
ws.append([1, 2])
44+
tab = Table(displayName="Table1", ref="A1:B2")
45+
ws.add_table(tab)
46+
wb.save(path)
47+
wb.close()
48+
49+
# Force density/coverage to high thresholds to ensure Table objects are still returned
50+
monkeypatch.setattr(cells, "_DETECTION_CONFIG", {
51+
"table_score_threshold": 0.99,
52+
"density_min": 0.99,
53+
"coverage_min": 0.99,
54+
"min_nonempty_cells": 1,
55+
})
56+
tables = detect_tables_openpyxl(path, "Sheet1")
57+
assert "A1:B2" in tables

tests/test_integrate_fallback.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from pathlib import Path
2+
3+
from openpyxl import Workbook
4+
5+
from exstruct.core import integrate
6+
7+
8+
def test_extract_workbook_fallback_on_com_failure(monkeypatch, tmp_path: Path) -> None:
9+
# create a tiny workbook
10+
xlsx = tmp_path / "sample.xlsx"
11+
wb = Workbook()
12+
ws = wb.active
13+
ws.title = "Sheet1"
14+
ws.append(["A", "B"])
15+
ws.append([1, 2])
16+
from openpyxl.worksheet.table import Table
17+
18+
ws.add_table(Table(displayName="Table1", ref="A1:B2"))
19+
wb.save(xlsx)
20+
wb.close()
21+
22+
def boom(_path):
23+
raise RuntimeError("COM unavailable")
24+
25+
monkeypatch.setattr(integrate, "_open_workbook", boom)
26+
result = integrate.extract_workbook(xlsx, mode="standard")
27+
assert result.sheets["Sheet1"].shapes == []
28+
assert result.sheets["Sheet1"].charts == []
29+
# table_candidates populated via openpyxl detection fallback
30+
assert result.sheets["Sheet1"].table_candidates

tests/test_io_utils.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from exstruct.io import dict_without_empty_values
2+
3+
4+
def test_dict_without_empty_values_nested() -> None:
5+
data = {
6+
"a": 1,
7+
"b": "",
8+
"c": [],
9+
"d": {},
10+
"e": None,
11+
"f": {"x": "ok", "y": "", "z": {"k": None, "m": 2}},
12+
"g": [1, "", {}, []],
13+
}
14+
filtered = dict_without_empty_values(data)
15+
assert filtered == {"a": 1, "f": {"x": "ok", "z": {"m": 2}}, "g": [1]}

0 commit comments

Comments
 (0)