Initial commit

Add spruce scraper with CLI, session management, parsers, progress tracking,
recheck logic, and test suite. Includes example config and README.
This commit is contained in:
2026-04-22 10:41:18 -04:00
commit e122f6435a
23 changed files with 3789 additions and 0 deletions
+153
View File
@@ -0,0 +1,153 @@
"""Tests for spruce.parsers — all pure, no network required."""
import pytest
from spruce.parsers import (
_grid_count,
_grid_values,
parse_machine_option,
parse_scan_row,
parse_scan_view,
)
# ---------------------------------------------------------------------------
# parse_machine_option
# ---------------------------------------------------------------------------
def test_parse_machine_option_basic():
raw = "BW3%2D20%20%5BAMR%2D26%5D|205.149.147.130|17026|26|8026|3.0.0.33|50.00"
m = parse_machine_option("BW3-20 [AMR-26]", raw)
assert m["label"] == "BW3-20 [AMR-26]"
assert m["machine_id"] == "26"
assert m["ip"] == "205.149.147.130"
assert m["version"] == "3.0.0.33"
assert m["option_value"] == raw
def test_parse_machine_option_short_value():
# Fewer pipe-delimited parts should not raise; missing fields return ""
m = parse_machine_option("Lonely Machine", "LM|10.0.0.1")
assert m["ip"] == "10.0.0.1"
assert m["machine_id"] == ""
assert m["port2"] == ""
# ---------------------------------------------------------------------------
# parse_scan_row
# ---------------------------------------------------------------------------
VALID_CELLS = [
"158374",
"Plot 20 AMR26 Full Tube Scan",
"2024-07-29 05:00",
"mm",
"(0,0)-(310,740)-(3.01,2.26)",
"100",
"Horizontal",
"Raster",
"2024-07-29 04:59:46",
"2024-07-30 02:51:07",
"0",
"SPRUCE",
"Completed",
"X",
]
def test_parse_scan_row_valid():
sc = parse_scan_row(VALID_CELLS)
assert sc is not None
assert sc["scan_id"] == 158374
assert sc["name"] == "Plot 20 AMR26 Full Tube Scan"
assert sc["status"] == "Completed"
assert sc["scan_time"] == "2024-07-29 05:00"
def test_parse_scan_row_ignores_non_digit_first_cell():
assert parse_scan_row(["ID", "Name", "Scan Time"]) is None
assert parse_scan_row(["Scan ID:", "158374"]) is None
def test_parse_scan_row_empty():
assert parse_scan_row([]) is None
def test_parse_scan_row_minimal():
sc = parse_scan_row(["42"])
assert sc is not None
assert sc["scan_id"] == 42
assert sc["name"] == ""
# ---------------------------------------------------------------------------
# parse_scan_view (uses fixture HTML from conftest.py)
# ---------------------------------------------------------------------------
def test_parse_scan_view_scan_id(scan_view_html):
meta = parse_scan_view(scan_view_html)
assert meta.get("scan_id") == 158374
def test_parse_scan_view_grid_dimensions(scan_view_html):
meta = parse_scan_view(scan_view_html)
assert meta.get("nx") == 103
assert meta.get("ny") == 328
def test_parse_scan_view_step_sizes(scan_view_html):
meta = parse_scan_view(scan_view_html)
assert meta.get("dx") == pytest.approx(3.01)
assert meta.get("dy") == pytest.approx(2.26)
def test_parse_scan_view_total_tiles(scan_view_html):
meta = parse_scan_view(scan_view_html)
# 103 × 328 = 33784
assert meta.get("total_tiles") == 103 * 328
def test_parse_scan_view_empty_string():
meta = parse_scan_view("")
assert meta == {}
# ---------------------------------------------------------------------------
# _grid_count
# ---------------------------------------------------------------------------
def test_grid_count_typical():
assert _grid_count(0, 310, 3.01) == 103
assert _grid_count(0, 740, 2.26) == 328
def test_grid_count_zero_step():
assert _grid_count(0, 100, 0) == 0
def test_grid_count_single():
assert _grid_count(0, 1, 1) == 1
# ---------------------------------------------------------------------------
# _grid_values
# ---------------------------------------------------------------------------
def test_grid_values_basic():
vals = _grid_values(0.0, 3, 3.01)
assert vals == [0.0, 3.01, 6.02]
def test_grid_values_empty():
assert _grid_values(0.0, 0, 1.0) == []
def test_grid_values_rounded():
# Floating-point accumulation should be rounded to 2 dp
vals = _grid_values(0.0, 4, 0.1)
for v in vals:
assert v == round(v, 2)