Tighten Auto-Match refs and log accept/reject (Phase 2).
CI / skip-ci-check (pull_request) Successful in 30s
CI / python-lint (pull_request) Successful in 31s
CI / docker-ci (pull_request) Successful in 32s
CI / secret-scan (pull_request) Successful in 35s
CI / e2e (pull_request) Successful in 2m34s
CI / viewer-unit (pull_request) Successful in 2m53s
CI / admin-unit (pull_request) Successful in 3m8s
CI / skip-ci-check (pull_request) Successful in 30s
CI / python-lint (pull_request) Successful in 31s
CI / docker-ci (pull_request) Successful in 32s
CI / secret-scan (pull_request) Successful in 35s
CI / e2e (pull_request) Successful in 2m34s
CI / viewer-unit (pull_request) Successful in 2m53s
CI / admin-unit (pull_request) Successful in 3m8s
Raise reference quality floor to 0.5, align browse/run/auto-accept defaults with UI copy, and persist match_decisions for later confidence calibration.
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
"""Unit tests for Auto-Match Phase 2 helpers (no DeepFace / DB required)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from types import SimpleNamespace
|
||||
|
||||
from backend.config import (
|
||||
DEFAULT_AUTO_ACCEPT_THRESHOLD,
|
||||
DEFAULT_BROWSE_TOLERANCE,
|
||||
DEFAULT_RUN_TOLERANCE,
|
||||
MIN_AUTO_MATCH_REFERENCE_QUALITY,
|
||||
)
|
||||
from backend.services.face_service import (
|
||||
pick_best_reference_face_per_person,
|
||||
record_match_decisions,
|
||||
)
|
||||
|
||||
|
||||
def _face(person_id: int | None, quality: float, face_id: int = 1):
|
||||
return SimpleNamespace(id=face_id, person_id=person_id, quality_score=quality)
|
||||
|
||||
|
||||
class TestPhase2ConfigDefaults:
|
||||
def test_reference_quality_stricter_than_legacy(self):
|
||||
assert MIN_AUTO_MATCH_REFERENCE_QUALITY >= 0.5
|
||||
|
||||
def test_auto_accept_aligns_with_ui_copy(self):
|
||||
assert DEFAULT_AUTO_ACCEPT_THRESHOLD >= 85.0
|
||||
|
||||
def test_run_tolerance_stricter_than_browse(self):
|
||||
assert DEFAULT_RUN_TOLERANCE < DEFAULT_BROWSE_TOLERANCE
|
||||
assert DEFAULT_BROWSE_TOLERANCE <= 0.5
|
||||
|
||||
|
||||
class TestPickBestReferenceFacePerPerson:
|
||||
def test_keeps_first_face_per_person(self):
|
||||
faces = [
|
||||
_face(1, 0.9, face_id=10),
|
||||
_face(1, 0.8, face_id=11),
|
||||
_face(2, 0.7, face_id=20),
|
||||
]
|
||||
picked = pick_best_reference_face_per_person(faces)
|
||||
assert set(picked.keys()) == {1, 2}
|
||||
assert picked[1].id == 10
|
||||
assert picked[2].id == 20
|
||||
|
||||
def test_skips_unidentified(self):
|
||||
faces = [_face(None, 0.9, face_id=1), _face(3, 0.6, face_id=3)]
|
||||
picked = pick_best_reference_face_per_person(faces)
|
||||
assert list(picked.keys()) == [3]
|
||||
assert picked[3].id == 3
|
||||
|
||||
def test_empty(self):
|
||||
assert pick_best_reference_face_per_person([]) == {}
|
||||
|
||||
|
||||
class TestRecordMatchDecisions:
|
||||
def test_bulk_adds_and_skips_missing_face_id(self):
|
||||
added: list = []
|
||||
|
||||
class FakeSession:
|
||||
def add(self, row):
|
||||
added.append(row)
|
||||
|
||||
def commit(self):
|
||||
pass
|
||||
|
||||
count = record_match_decisions(
|
||||
FakeSession(), # type: ignore[arg-type]
|
||||
decision="accept",
|
||||
source="auto_match",
|
||||
person_id=42,
|
||||
items=[
|
||||
{"face_id": 1, "similarity": 91.0, "distance": 0.2, "reference_face_id": 9},
|
||||
{"similarity": 50.0}, # missing face_id — skipped
|
||||
{"face_id": 2, "similarity": 88.0},
|
||||
],
|
||||
user_id=7,
|
||||
commit=True,
|
||||
)
|
||||
assert count == 2
|
||||
assert len(added) == 2
|
||||
assert added[0].decision == "accept"
|
||||
assert added[0].person_id == 42
|
||||
assert added[0].face_id == 1
|
||||
assert float(added[0].similarity) == 91.0
|
||||
assert added[0].user_id == 7
|
||||
assert added[1].face_id == 2
|
||||
|
||||
def test_reject_source(self):
|
||||
added: list = []
|
||||
|
||||
class FakeSession:
|
||||
def add(self, row):
|
||||
added.append(row)
|
||||
|
||||
def commit(self):
|
||||
pass
|
||||
|
||||
record_match_decisions(
|
||||
FakeSession(), # type: ignore[arg-type]
|
||||
decision="reject",
|
||||
source="auto_match",
|
||||
person_id=1,
|
||||
items=[{"face_id": 99, "similarity": 72.0}],
|
||||
commit=True,
|
||||
)
|
||||
assert added[0].decision == "reject"
|
||||
assert added[0].source == "auto_match"
|
||||
Reference in New Issue
Block a user