"""Tests for core/cam.py — CAM engine.""" from core.cam import CAMResult, ToolConfig class TestToolConfig: def test_defaults(self): tc = ToolConfig() assert tc.diameter == 6.0 assert tc.h_feed == 800 assert tc.v_feed == 200 assert tc.speed == 18000 def test_custom_values(self): tc = ToolConfig(diameter=3.0, h_feed=400, v_feed=100, speed=24000) assert tc.diameter == 3.0 assert tc.h_feed == 400 assert tc.v_feed == 100 assert tc.speed == 24000 def test_model_dump(self): tc = ToolConfig(diameter=10.0, h_feed=1000, v_feed=300, speed=12000) d = tc.model_dump() assert d == {"diameter": 10.0, "h_feed": 1000, "v_feed": 300, "speed": 12000} class TestCAMResult: def test_success_result(self): r = CAMResult( success=True, gcode="G21 G90\nG00 X0 Y0 Z10\nM30", operations=["pocket", "profile"], tool_config=ToolConfig(diameter=6, h_feed=800), post_processor="grbl", ) assert r.success is True assert "G21" in r.gcode assert r.operations == ["pocket", "profile"] assert r.error is None def test_failure_result(self): r = CAMResult(success=False, error="ocp-freecad-cam not available") assert r.success is False assert r.gcode is None assert r.error == "ocp-freecad-cam not available" def test_model_dump(self): r = CAMResult( success=True, gcode="G21 G90\nM30", operations=["pocket"], tool_config=ToolConfig(diameter=6), ) d = r.model_dump() assert d["success"] is True assert d["gcode"] == "G21 G90\nM30" assert d["operations"] == ["pocket"] assert d["tool_config"] == {"diameter": 6.0, "h_feed": 800, "v_feed": 200, "speed": 18000} assert d["post_processor"] == "grbl" assert d["error"] is None def test_default_tool_config_is_toolconfig(self): r = CAMResult(success=True) assert isinstance(r.tool_config, ToolConfig) from unittest.mock import patch, MagicMock from core.cam import generate_gcode class TestGenerateGcode: def test_returns_failure_when_ocp_not_available(self): """When ocp-freecad-cam is not installed, return a failure CAMResult.""" mock_shape = MagicMock() result = generate_gcode( shape=mock_shape, operations=["pocket"], tool_config=ToolConfig(diameter=6, h_feed=800, v_feed=200, speed=18000), post_processor="grbl", ) assert result.success is False assert "not available" in result.error.lower() or "not installed" in result.error.lower() def test_returns_failure_on_empty_operations(self): mock_shape = MagicMock() result = generate_gcode( shape=mock_shape, operations=[], ) assert result.success is False assert "no operations" in result.error.lower() def test_uses_default_tool_config_when_none(self): mock_shape = MagicMock() result = generate_gcode( shape=mock_shape, operations=["pocket"], tool_config=None, ) assert result.tool_config is not None assert isinstance(result.tool_config, ToolConfig) assert result.tool_config.diameter == 6.0 def test_uses_default_post_processor(self): mock_shape = MagicMock() result = generate_gcode(shape=mock_shape, operations=["pocket"]) assert result.post_processor == "grbl" def test_tool_config_attribute_access(self): mock_shape = MagicMock() tc = ToolConfig(diameter=3.0, h_feed=400, v_feed=100, speed=24000) result = generate_gcode( shape=mock_shape, operations=["pocket"], tool_config=tc, ) assert result.tool_config.diameter == 3.0 assert result.tool_config.h_feed == 400 from core.cam import CAMPlan class TestCAMPlan: def test_default_values(self): plan = CAMPlan(operations=["pocket", "profile"]) assert plan.operations == ["pocket", "profile"] assert plan.tool_diameter == 6.0 assert plan.tool_h_feed == 800 assert plan.tool_v_feed == 200 assert plan.tool_speed == 18000 assert plan.post_processor == "grbl" def test_custom_values(self): plan = CAMPlan( operations=["adaptive", "surface"], tool_diameter=3.0, tool_h_feed=400, tool_v_feed=100, tool_speed=24000, post_processor="linuxcnc", ) assert plan.tool_diameter == 3.0 assert plan.post_processor == "linuxcnc" def test_to_tool_config(self): plan = CAMPlan(operations=["pocket"]) config = plan.to_tool_config() assert isinstance(config, ToolConfig) assert config.diameter == 6.0 assert config.h_feed == 800 assert config.v_feed == 200 assert config.speed == 18000