117 lines
4.6 KiB
Python
117 lines
4.6 KiB
Python
"""Unit-Tests für Spulenwahl und Steuer-Parameter-Aliase."""
|
|
|
|
import unittest
|
|
from datetime import datetime, timedelta, timezone
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from grotti import jobs
|
|
from grotti import moonraker
|
|
|
|
|
|
class ChooseSpoolTests(unittest.TestCase):
|
|
def test_rejects_spool_created_after_print_end(self):
|
|
end = datetime(2026, 10, 1, 12, 0, tzinfo=timezone.utc)
|
|
old = {
|
|
"id": 1,
|
|
"material_type": "PLA",
|
|
"created_at": end - timedelta(days=2),
|
|
}
|
|
new = {
|
|
"id": 2,
|
|
"material_type": "PLA",
|
|
"created_at": end + timedelta(hours=1),
|
|
}
|
|
conn = MagicMock()
|
|
with patch("grotti.jobs.store.spools_for_printer", return_value=[new]):
|
|
with patch("grotti.jobs.store.get_spool_row", return_value=new):
|
|
self.assertIsNone(jobs.choose_spool(conn, "h", "PLA", 2, ended_at=end))
|
|
with patch("grotti.jobs.store.spools_for_printer", return_value=[old]):
|
|
self.assertEqual(jobs.choose_spool(conn, "h", "PLA", None, ended_at=end)["id"], 1)
|
|
|
|
def test_prefers_host_spool_over_stale_hint(self):
|
|
"""Rollenwechsel: Hint im Regal, neue Rolle am Drucker → Abzug auf die neue."""
|
|
end = datetime(2026, 10, 18, 14, 58, tzinfo=timezone.utc)
|
|
hinted = {
|
|
"id": 4,
|
|
"material_type": "PLA",
|
|
"printer_host": "",
|
|
"created_at": end - timedelta(days=5),
|
|
}
|
|
current = {
|
|
"id": 14,
|
|
"material_type": "PLA",
|
|
"printer_host": "h",
|
|
"created_at": end - timedelta(minutes=10),
|
|
}
|
|
conn = MagicMock()
|
|
with patch("grotti.jobs.store.spools_for_printer", return_value=[current]):
|
|
with patch("grotti.jobs.store.get_spool_row", return_value=hinted):
|
|
self.assertEqual(jobs.choose_spool(conn, "h", "PLA", 4, ended_at=end)["id"], 14)
|
|
|
|
def test_keeps_hint_when_no_host_spool(self):
|
|
end = datetime(2026, 10, 18, 14, 58, tzinfo=timezone.utc)
|
|
hinted = {
|
|
"id": 4,
|
|
"material_type": "PLA",
|
|
"printer_host": "",
|
|
"created_at": end - timedelta(days=5),
|
|
}
|
|
conn = MagicMock()
|
|
with patch("grotti.jobs.store.spools_for_printer", return_value=[]):
|
|
with patch("grotti.jobs.store.get_spool_row", return_value=hinted):
|
|
self.assertEqual(jobs.choose_spool(conn, "h", "PLA", 4, ended_at=end)["id"], 4)
|
|
|
|
def test_resolve_tracking_spool_prefers_host(self):
|
|
conn = MagicMock()
|
|
with patch(
|
|
"grotti.jobs.resolve_spools_at_host",
|
|
return_value=[{"id": 14, "printer_host": "h"}],
|
|
):
|
|
self.assertEqual(jobs.resolve_tracking_spool(conn, "h", 4), 14)
|
|
|
|
def test_ace_target_skips_spool_created_after_end(self):
|
|
end = datetime(2026, 10, 15, 22, 0, tzinfo=timezone.utc)
|
|
future = {"id": 18, "created_at": end + timedelta(days=6)}
|
|
conn = MagicMock()
|
|
with patch("grotti.jobs.store.spool_at_slot", return_value=future):
|
|
self.assertEqual(
|
|
jobs._targets_from_ace(conn, "h", [{"ams_index": 3}], ended_at=end),
|
|
[],
|
|
)
|
|
|
|
def test_spool_valid_for_end_naive(self):
|
|
end = datetime(2026, 10, 1, 12, 0, tzinfo=timezone.utc)
|
|
spool = {"created_at": datetime(2026, 9, 1, 12, 0)}
|
|
self.assertTrue(jobs._spool_valid_for_end(spool, end))
|
|
|
|
|
|
class SteuernAliasTests(unittest.TestCase):
|
|
@patch("grotti.moonraker.gcode_script", return_value={"ok": True})
|
|
def test_value_alias_for_temp(self, gcode):
|
|
moonraker.steuern("10.0.0.1", "nozzle", {"value": 210})
|
|
script = gcode.call_args[0][1]
|
|
self.assertIn("TARGET=210.0", script)
|
|
|
|
@patch("grotti.moonraker.gcode_script", return_value={"ok": True})
|
|
def test_percent_alias_for_speed(self, gcode):
|
|
moonraker.steuern("10.0.0.1", "speed", {"value": 120})
|
|
script = gcode.call_args[0][1]
|
|
self.assertIn("M220 S120", script)
|
|
|
|
|
|
class HostValidationTests(unittest.TestCase):
|
|
def test_validate_printer_host(self):
|
|
from web.app import validate_printer_host
|
|
from grotti.errors import GrottiError
|
|
|
|
self.assertEqual(validate_printer_host("10.10.1.60"), "10.10.1.60")
|
|
self.assertEqual(validate_printer_host("kobra.local:80"), "kobra.local:80")
|
|
with self.assertRaises(GrottiError):
|
|
validate_printer_host("http://evil")
|
|
with self.assertRaises(GrottiError):
|
|
validate_printer_host("../etc/passwd")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|