# deck-it - tests for the K1C printer module # Copyright (C) 2026 Danilo M. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) import unittest import deckit.modules.k1c as k1c URL = "http://printer.example.org:7125" STATUS = { "webhooks": {"state": "ready", "state_message": "Printer is ready"}, "print_stats": {"state": "printing", "filename": "part.gcode", "print_duration": 600.0, "filament_used": 1234.5, "info": {"current_layer": 12, "total_layer": 80}}, "virtual_sdcard": {"progress": 0.25}, "extruder": {"temperature": 219.8, "target": 220.0, "power": 0.4}, "heater_bed": {"temperature": 60.1, "target": 60.0, "power": 0.2}, "fan": {"speed": 1.0}, "toolhead": {"position": [10.0, 20.0, 3.2, 100.0]}, "gcode_move": {"speed_factor": 1.0}, } CAMS = {"result": {"webcams": [{"enabled": True, "stream_url": "http://printer.example.org:8080/?action=stream"}]}} class Fake: def __init__(self, down=False): self.calls, self.down = [], down def __call__(self, url, data=None, timeout=2): self.calls.append((url, data)) if self.down: raise OSError("connection refused") if url.endswith("/server/webcams/list"): return CAMS if "/server/files/metadata" in url: return {"result": {"estimated_time": 2700, "thumbnails": [ {"width": 32, "height": 32, "relative_path": ".thumbs/part-32x32.png"}, {"width": 300, "height": 300, "relative_path": ".thumbs/part-300x300.png"}]}} if url.endswith("/printer/emergency_stop"): return {"result": "ok"} return {"result": {"status": STATUS}} class K1C(unittest.TestCase): def test_summarizes_a_print(self): st = k1c.make({"k1c": {"url": URL}, "http": Fake()}).state() self.assertEqual(st["state"], "printing") self.assertEqual(st["remaining"], 1800.0) # 600s for 25%: 1800s to go self.assertEqual((st["layer"], st["layers"]), (12, 80)) self.assertEqual(st["extruder"]["target"], 220.0) self.assertEqual(st["position"], [10.0, 20.0, 3.2]) self.assertTrue(st["camera"].endswith("action=stream")) self.assertEqual(st["thumbnail"], URL + "/server/files/gcodes/.thumbs/part-300x300.png") def test_thumbnail_is_relative_to_the_file_and_quoted(self): url = k1c._thumbnail(URL, "jobs/my part.gcode", {"thumbnails": [{"width": 9, "height": 9, "relative_path": ".thumbs/my part-9x9.png"}]}) self.assertEqual(url, URL + "/server/files/gcodes/jobs/.thumbs/my%20part-9x9.png") self.assertIsNone(k1c._thumbnail(URL, "x.gcode", {})) def test_metadata_is_fetched_once_per_file(self): fake = Fake() m = k1c.make({"k1c": {"url": URL}, "http": fake}) m.state(), m.state() self.assertEqual(sum("/server/files/metadata" in u for u, _ in fake.calls), 1) def test_part_fan_falls_back_to_crealitys_output_pin(self): st = k1c.summarize({"fan": {}, "output_pin fan0": {"value": 0.5}}) self.assertEqual(st["fan"], 0.5) # What the K1C actually reports: the key present, the value null. self.assertEqual(k1c.summarize({"fan": {"speed": None}, "output_pin fan0": {"value": 1.0}})["fan"], 1.0) self.assertEqual(k1c.summarize({"fan": {"speed": 0.3}, "output_pin fan0": {"value": 1}})["fan"], 0.3) def test_off_backs_off_instead_of_polling_every_tick(self): fake = Fake(down=True) m = k1c.make({"k1c": {"url": URL}, "http": fake}) self.assertTrue(m.state()["offline"]) self.assertTrue(m.state()["offline"]) self.assertEqual(len(fake.calls), 1) def test_unconfigured(self): self.assertTrue(k1c.make({}).state()["unconfigured"]) def test_emergency_stop_posts_m112(self): fake = Fake() k1c.make({"k1c": {"url": URL}, "http": fake}).actions["emergency_stop"]({}) self.assertEqual(fake.calls, [(URL + "/printer/emergency_stop", b"")]) def test_emergency_stop_failure_is_reported(self): m = k1c.make({"k1c": {"url": URL}, "http": Fake(down=True)}) with self.assertRaises(k1c.ModuleError): m.actions["emergency_stop"]({}) def test_camera_needs_http(self): self.assertIsNone(k1c._camera([{"stream_url": "javascript:alert(1)"}])) if __name__ == "__main__": unittest.main()