119 lines
4.0 KiB
Python
119 lines
4.0 KiB
Python
"""Unit tests for history export parsing (exporting.history).
|
|
|
|
The commit-message grammar is defined by message_builder (Section 15);
|
|
these tests pin the parser against it.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from git_auto_tracker.exporting.history import (
|
|
HistoryRecord,
|
|
parse_message,
|
|
to_rows,
|
|
)
|
|
|
|
_MESSAGE = """SCADA automatic change capture - 2026-09-12 08:00:03+02:00
|
|
|
|
Summary:
|
|
Added: 1
|
|
Modified: 1
|
|
Deleted: 1
|
|
Renamed: 0
|
|
|
|
Changes:
|
|
A ; Stations/PS101/pump2.xml ; DOMAIN\\jsmith ; 2026-09-12 07:41:22+02:00
|
|
M ; System/global.xml ; UNKNOWN ; 2026-09-12 07:45:01+02:00
|
|
D ; Stations/PS027/old-config.xml
|
|
|
|
Tracker-Version: 1.0.0
|
|
Tracker-Run: 01M2ADPMSHSA2TZKVH6PSVDVA0
|
|
Tracker-Host: SCADA-SRV-01
|
|
Tracker-Changes: 3
|
|
"""
|
|
|
|
|
|
class TestParseMessage:
|
|
def test_full_message(self) -> None:
|
|
record = parse_message(_MESSAGE, ";")
|
|
assert record is not None
|
|
assert record.collected_at == "2026-09-12 08:00:03+02:00"
|
|
assert record.run_id == "01M2ADPMSHSA2TZKVH6PSVDVA0"
|
|
assert record.host == "SCADA-SRV-01"
|
|
assert len(record.rows) == 3
|
|
|
|
added = record.rows[0]
|
|
assert added["letter"] == "A"
|
|
assert added["path"] == "Stations/PS101/pump2.xml"
|
|
assert added["author"] == "DOMAIN\\jsmith"
|
|
assert added["mtime"] == "2026-09-12 07:41:22+02:00"
|
|
|
|
modified = record.rows[1]
|
|
assert modified["letter"] == "M"
|
|
assert modified["author"] == "UNKNOWN"
|
|
|
|
deleted = record.rows[2]
|
|
assert deleted["letter"] == "D"
|
|
assert deleted["path"] == "Stations/PS027/old-config.xml"
|
|
assert deleted["author"] == "" # bare line (FR-013)
|
|
|
|
def test_paths_with_semicolon_inside_are_not_broken(self) -> None:
|
|
# A path containing ';' is ambiguous by design; the parser splits on
|
|
# ' ; ' so a lone ';' inside a filename keeps the line intact here.
|
|
message = _MESSAGE.replace(
|
|
"Stations/PS101/pump2.xml", " Stations/PS101;backup.xml".strip()
|
|
)
|
|
record = parse_message(message, ";")
|
|
assert record is not None
|
|
assert record.rows[0]["path"] == "Stations/PS101;backup.xml"
|
|
|
|
def test_non_tracker_message_returns_none(self) -> None:
|
|
assert parse_message("initial import\n", ";") is None
|
|
assert parse_message("", ";") is None
|
|
|
|
def test_custom_separator(self) -> None:
|
|
message = _MESSAGE.replace(" ; ", " | ")
|
|
record = parse_message(message, "|")
|
|
assert record is not None
|
|
assert len(record.rows) == 3
|
|
assert record.rows[0]["path"] == "Stations/PS101/pump2.xml"
|
|
|
|
def test_wrong_separator_yields_no_rows_but_is_tracker_commit(self) -> None:
|
|
# Written with ';', parsed with '|': recognized but not splittable.
|
|
record = parse_message(_MESSAGE, "|")
|
|
assert record is not None
|
|
assert record.collected_at # tracker commit detected
|
|
assert record.rows == [] # but no reliable rows
|
|
|
|
def test_rename_line(self) -> None:
|
|
message = _MESSAGE.replace(
|
|
" D ; Stations/PS027/old-config.xml",
|
|
" R ; Stations/PS027/old.xml -> Stations/PS027/new.xml",
|
|
)
|
|
record = parse_message(message, ";")
|
|
assert record is not None
|
|
row = record.rows[2]
|
|
assert row["letter"] == "R"
|
|
assert row["previous_path"] == "Stations/PS027/old.xml"
|
|
assert row["path"] == "Stations/PS027/new.xml"
|
|
|
|
|
|
class TestToRows:
|
|
def test_long_format(self) -> None:
|
|
record = parse_message(_MESSAGE, ";")
|
|
assert record is not None
|
|
record.commit = "a1b2c3d"
|
|
rows = to_rows([record])
|
|
assert len(rows) == 3
|
|
first = rows[0]
|
|
assert first["commit"] == "a1b2c3d"
|
|
assert first["run_id"] == "01M2ADPMSHSA2TZKVH6PSVDVA0"
|
|
assert first["letter"] == "A"
|
|
assert set(first) == {
|
|
"commit", "collected_at", "run_id", "host",
|
|
"letter", "path", "previous_path", "author", "mtime",
|
|
}
|
|
|
|
def test_empty(self) -> None:
|
|
assert to_rows([]) == []
|
|
assert to_rows([HistoryRecord(commit="x")]) == []
|