"""Unit tests for src/server/target_point.py""" import pytest from src.server.target_point import ( _poly_to_abs, _shape_center, _pin_left_midpoint, _pick_best, compute_target_point, ) # --------------------------------------------------------------------------- # _poly_to_abs # --------------------------------------------------------------------------- class TestPolyToAbs: def test_none_poly_returns_none(self): assert _poly_to_abs(None, 10, 20) is None def test_translates_relative_to_absolute(self): poly = [[1, 2], [3, 4], [5, 6]] result = _poly_to_abs(poly, 10.0, 20.0) assert result is not None assert result[0, 0] == pytest.approx(11.0) assert result[0, 1] == pytest.approx(22.0) assert result[2, 0] == pytest.approx(15.0) def test_rejects_fewer_than_3_points(self): poly = [[1, 2], [3, 4]] assert _poly_to_abs(poly, 0, 0) is None def test_rejects_wrong_shape(self): poly = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] assert _poly_to_abs(poly, 0, 0) is None # --------------------------------------------------------------------------- # _shape_center # --------------------------------------------------------------------------- class TestShapeCenter: def test_falls_back_to_bbox_center_with_no_poly(self): det = {"x1": 0.0, "y1": 0.0, "x2": 100.0, "y2": 100.0} cx, cy = _shape_center(det) assert cx == pytest.approx(50.0) assert cy == pytest.approx(50.0) def test_uses_polygon_centroid_when_poly_present(self): # Unit square at origin: centroid == (0.5, 0.5) + offset (10, 20) = (10.5, 20.5) poly = [[0, 0], [1, 0], [1, 1], [0, 1]] det = {"x1": 10.0, "y1": 20.0, "x2": 11.0, "y2": 21.0, "poly": poly} cx, cy = _shape_center(det) assert cx == pytest.approx(10.5, abs=0.1) assert cy == pytest.approx(20.5, abs=0.1) def test_returns_none_on_missing_coords(self): assert _shape_center({}) == (0.0, 0.0) # --------------------------------------------------------------------------- # _pin_left_midpoint # --------------------------------------------------------------------------- class TestPinLeftMidpoint: def test_returns_x1_and_vertical_midpoint(self): det = {"x1": 5.0, "y1": 10.0, "x2": 50.0, "y2": 30.0} x, y = _pin_left_midpoint(det) assert x == pytest.approx(5.0) assert y == pytest.approx(20.0) def test_returns_none_on_bad_coords(self): assert _pin_left_midpoint({"x1": "bad", "y1": 0, "y2": 10}) is None # --------------------------------------------------------------------------- # _pick_best # --------------------------------------------------------------------------- class TestPickBest: def test_returns_none_when_no_matching_label(self): dets = [{"label": "pin", "conf": 0.9}] assert _pick_best(dets, "crystal") is None def test_returns_highest_confidence_detection(self): dets = [ {"label": "crystal", "conf": 0.5}, {"label": "crystal", "conf": 0.9}, {"label": "crystal", "conf": 0.3}, ] best = _pick_best(dets, "crystal") assert best["conf"] == pytest.approx(0.9) def test_returns_none_for_empty_list(self): assert _pick_best([], "crystal") is None # --------------------------------------------------------------------------- # compute_target_point — priority logic # --------------------------------------------------------------------------- class TestComputeTargetPoint: def test_returns_none_for_empty_dets(self): assert compute_target_point([]) is None def test_crystal_takes_priority_over_loop_face(self): dets = [ {"label": "loop_face", "conf": 0.99, "x1": 0, "y1": 0, "x2": 200, "y2": 200}, {"label": "crystal", "conf": 0.5, "x1": 10, "y1": 10, "x2": 50, "y2": 50}, ] result = compute_target_point(dets) assert result is not None assert result["source"] == "crystal_center" def test_loop_face_takes_priority_over_loop_all(self): dets = [ {"label": "loop_all", "conf": 0.99, "x1": 0, "y1": 0, "x2": 200, "y2": 200}, {"label": "loop_face", "conf": 0.5, "x1": 10, "y1": 10, "x2": 50, "y2": 50}, ] result = compute_target_point(dets) assert result is not None assert result["source"] == "loop_face_center" def test_pin_used_as_last_resort(self): dets = [{"label": "pin", "conf": 0.8, "x1": 20.0, "y1": 10.0, "x2": 80.0, "y2": 30.0}] result = compute_target_point(dets) assert result is not None assert result["source"] == "pin_left_mid" assert result["x"] == 20 assert result["y"] == 20 def test_output_contains_integer_coordinates(self): dets = [{"label": "pin", "conf": 0.8, "x1": 10.7, "y1": 5.3, "x2": 60.0, "y2": 25.1}] result = compute_target_point(dets) assert isinstance(result["x"], int) assert isinstance(result["y"], int) def test_unknown_labels_return_none(self): dets = [{"label": "unknown_object", "conf": 0.99, "x1": 0, "y1": 0, "x2": 100, "y2": 100}] assert compute_target_point(dets) is None