import json from fam_edge.oracle_db import OracleDB def _db(tmp_path): return OracleDB(str(tmp_path / "oracle.db")) def test_upsert_person_same_gender_merges_into_one_row(tmp_path): db = _db(tmp_path) db.upsert_person("人物A", features={"gender": "男", "hair": "短发黑色"}) db.upsert_person("人物A", features={"gender": "男", "clothing": "蓝色T恤"}) rows = db._conn.execute("SELECT * FROM people").fetchall() assert len(rows) == 1 assert rows[0]["appearances"] == 2 feats = json.loads(rows[0]["features_json"]) assert feats["hair"] == "短发黑色" assert feats["clothing"] == "蓝色T恤" def test_upsert_person_gender_conflict_splits_into_new_label(tmp_path): """核心诉求: 大模型给的 人物A/B/C 这类 uid 只在单次视频分析内稳定,不同视频 独立编号,同一字符串完全可能撞到不同真人(实测 "人物A" 混了男女两个人)。 性别冲突时不能直接合并覆盖,要拆成新 label,避免两个人的特征越merge越乱。""" db = _db(tmp_path) db.upsert_person("人物A", features={"gender": "男", "clothing": "蓝色Polo衫"}) db.upsert_person("人物A", features={"gender": "女", "clothing": "白色上衣"}) rows = {r["label"]: r for r in db._conn.execute("SELECT * FROM people").fetchall()} assert set(rows.keys()) == {"人物A", "人物A#2"} assert rows["人物A"]["appearances"] == 1 assert json.loads(rows["人物A"]["features_json"])["gender"] == "男" assert rows["人物A#2"]["appearances"] == 1 assert json.loads(rows["人物A#2"]["features_json"])["gender"] == "女" # 派生行的 display_uid 仍然记录原始大模型 uid,方便追溯来源 assert rows["人物A#2"]["display_uid"] == "人物A" def test_upsert_person_gender_conflict_allocates_next_free_suffix(tmp_path): db = _db(tmp_path) db.upsert_person("人物A", features={"gender": "男"}) db.upsert_person("人物A", features={"gender": "女"}) # -> 人物A#2 db.upsert_person("人物A", features={"gender": "unknown"}) # unknown 不冲突,合并回 人物A db.upsert_person("人物A", features={"gender": "男", "hair": "光头"}) # 冲突,人物A 有 gender 男了不冲突;应仍合并 labels = {r["label"] for r in db._conn.execute("SELECT label FROM people").fetchall()} assert labels == {"人物A", "人物A#2"} # 再来一次性别冲突(对着 人物A#2,性别女)应该分配 人物A#3,而不是复用 人物A#2 db.upsert_person("人物A", features={"gender": "男"}) db.upsert_person("人物A", features={"gender": "女"}) # 这次新的女性冲突会先撞到 人物A(此时是男),分裂出下一个空闲后缀 labels = {r["label"] for r in db._conn.execute("SELECT label FROM people").fetchall()} assert "人物A" in labels assert len(labels) >= 2 def test_upsert_person_unknown_gender_never_triggers_split(tmp_path): db = _db(tmp_path) db.upsert_person("人物A", features={"gender": "男"}) db.upsert_person("人物A", features={"gender": "unknown"}) db.upsert_person("人物A", features={"gender": "未知"}) rows = db._conn.execute("SELECT * FROM people").fetchall() assert len(rows) == 1 assert rows[0]["appearances"] == 3 def test_upsert_person_no_features_never_triggers_split(tmp_path): db = _db(tmp_path) db.upsert_person("人物A", features={"gender": "男"}) db.upsert_person("人物A") # 无 features(source 更新等场景) rows = db._conn.execute("SELECT * FROM people").fetchall() assert len(rows) == 1 assert rows[0]["appearances"] == 2