CI / test (push) Canceled after 0s
fix các bug theo yêu cầu https://fptsoftware362-my.sharepoint.com/❌/g/personal/nampdt_fpt_com/IQAHBJ4A9xqDTLgvt2bhukJEAdRB5LRz2hbJpTivvIiBSYM?wdExp=TEAMS-TREATMENT&web=1&isSPOFile=1&ovuser=f01e930a-b52e-42b1-b70f-a8882b5d043b%2CAnhTNM1%40fpt.com&clickparams=eyJBcHBOYW1lIjoiVGVhbXMtRGVza3RvcCIsIkFwcFZlcnNpb24iOiI0OS8yNjA4MTMxOTMxNyIsIkhhc0ZlZGVyYXRlZFVzZXIiOmZhbHNlfQ%3D%3D --------- Co-authored-by: Duy Le Huu <duylh19@fpt.com> Reviewed-on: #10 Co-authored-by: Anh Tran Nguyen Minh <anhtnm1@fpt.com>
159 lines
5.9 KiB
Python
159 lines
5.9 KiB
Python
"""Lịch sử hội thoại nằm trong thư mục của TỪNG project, không nằm chung.
|
|
|
|
``WorkspaceTab._bind_project`` đặt ``config._project_history_dir`` thành
|
|
``<workspace của project>/.cowork_history`` mỗi lần người dùng chọn project khác.
|
|
Hệ quả: một lần gọi ``list_conversations()`` chỉ thấy hội thoại của project đang
|
|
mở — và gọi KHÔNG tham số thì không thấy cái nào cả, vì nó đọc ``HISTORY_DIR``
|
|
toàn cục.
|
|
|
|
Hai lỗi đã xảy ra vì đúng chuyện này:
|
|
|
|
* Khung "Tất cả project…" dựng đủ tiêu đề nhóm cho mọi project nhưng mọi nhóm
|
|
trừ một đều rỗng.
|
|
* Mọi dòng project đều đếm "0 đoạn chat · 0 task" dù người dùng đã chat.
|
|
|
|
Test không chạm ``~/.cowork_local``: ``core/projects.py`` gắn ``PROJECTS_DIR``
|
|
vào thư mục dữ liệu THẬT, nên mọi thứ ở đây dùng ``tmp_path`` và fake.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from cowork_local.core.history import (
|
|
list_conversations, list_conversations_by_project,
|
|
)
|
|
|
|
|
|
def _viet_hoi_thoai(directory: Path, session_id: str, title: str,
|
|
project_id: str = "", mtime: float | None = None) -> Path:
|
|
"""Ghi một file hội thoại tối thiểu mà ``list_conversations`` đọc được."""
|
|
directory.mkdir(parents=True, exist_ok=True)
|
|
path = directory / f"{session_id}.json"
|
|
path.write_text(json.dumps({
|
|
"kind": "cowork",
|
|
"title": title,
|
|
"created": "2026-09-07T10:00:00",
|
|
"session_id": session_id,
|
|
"project_id": project_id,
|
|
"messages": [{"role": "user", "content": title}],
|
|
}), encoding="utf-8")
|
|
if mtime is not None:
|
|
import os
|
|
os.utime(path, (mtime, mtime))
|
|
return path
|
|
|
|
|
|
@pytest.fixture
|
|
def hai_project(tmp_path):
|
|
"""Hai project, mỗi cái một đoạn chat — đúng tình huống người dùng báo."""
|
|
a = tmp_path / "test" / ".cowork_history"
|
|
b = tmp_path / "test1" / ".cowork_history"
|
|
_viet_hoi_thoai(a, "s-a", "chat cua test", mtime=1000)
|
|
_viet_hoi_thoai(b, "s-b", "chat cua test1", mtime=2000)
|
|
return [("p-test", a), ("p-test1", b)]
|
|
|
|
|
|
# ---- triệu chứng gốc -----------------------------------------------------
|
|
|
|
def test_mot_lan_goi_chi_thay_mot_project(hai_project):
|
|
"""Chốt lại chính nguyên nhân, để nó không bị coi là chuyện đương nhiên."""
|
|
_pid_a, dir_a = hai_project[0]
|
|
|
|
chi_mot = list_conversations(dir_a)
|
|
|
|
assert len(chi_mot) == 1
|
|
assert chi_mot[0]["title"] == "chat cua test"
|
|
|
|
|
|
def test_gop_nhieu_thu_muc_thi_thay_du(hai_project):
|
|
tat_ca = list_conversations_by_project(hai_project)
|
|
|
|
assert {c["title"] for c in tat_ca} == {"chat cua test", "chat cua test1"}
|
|
|
|
|
|
def test_moi_hoi_thoai_thuoc_dung_project(hai_project):
|
|
"""Thư mục là chủ sở hữu có thẩm quyền, không phải trường project_id trong file."""
|
|
theo_pid = {c["project_id"]: c["title"] for c in list_conversations_by_project(hai_project)}
|
|
|
|
assert theo_pid == {"p-test": "chat cua test", "p-test1": "chat cua test1"}
|
|
|
|
|
|
def test_project_id_cu_trong_file_bi_ghi_de(tmp_path):
|
|
"""Project bị đổi thư mục thì trường trong file đã cũ — thư mục vẫn đúng."""
|
|
d = tmp_path / "moi" / ".cowork_history"
|
|
_viet_hoi_thoai(d, "s1", "x", project_id="pid-cu-roi")
|
|
|
|
ket_qua = list_conversations_by_project([("pid-that", d)])
|
|
|
|
assert ket_qua[0]["project_id"] == "pid-that"
|
|
|
|
|
|
# ---- thứ tự và trùng lặp -------------------------------------------------
|
|
|
|
def test_giu_dung_thu_tu_moi_nhat_truoc(hai_project):
|
|
tat_ca = list_conversations_by_project(hai_project)
|
|
|
|
assert [c["title"] for c in tat_ca] == ["chat cua test1", "chat cua test"]
|
|
|
|
|
|
def test_ghim_len_dau_bat_ke_thoi_gian(tmp_path):
|
|
d1 = tmp_path / "a" / ".cowork_history"
|
|
d2 = tmp_path / "b" / ".cowork_history"
|
|
_viet_hoi_thoai(d1, "cu", "cu ma ghim", mtime=1000)
|
|
_viet_hoi_thoai(d2, "moi", "moi ma khong ghim", mtime=9000)
|
|
data = json.loads((d1 / "cu.json").read_text(encoding="utf-8"))
|
|
data["pinned"] = True
|
|
(d1 / "cu.json").write_text(json.dumps(data), encoding="utf-8")
|
|
|
|
tat_ca = list_conversations_by_project([("a", d1), ("b", d2)])
|
|
|
|
assert tat_ca[0]["title"] == "cu ma ghim"
|
|
|
|
|
|
def test_cung_mot_thu_muc_hai_lan_khong_dem_doi(hai_project):
|
|
"""``history_dirs()`` có thể trả về trùng thư mục khi cấu hình chồng nhau."""
|
|
_pid, d = hai_project[0]
|
|
|
|
assert len(list_conversations_by_project([("x", d), ("y", d)])) == 1
|
|
|
|
|
|
# ---- đầu vào xấu ---------------------------------------------------------
|
|
|
|
def test_thu_muc_chua_ton_tai_thi_bo_qua(tmp_path, hai_project):
|
|
tat_ca = list_conversations_by_project(
|
|
hai_project + [("rong", tmp_path / "chua-he-co")])
|
|
|
|
assert len(tat_ca) == 2
|
|
|
|
|
|
def test_thu_muc_None_thi_bo_qua(hai_project):
|
|
assert len(list_conversations_by_project(hai_project + [("x", None)])) == 2
|
|
|
|
|
|
def test_khong_co_project_nao_thi_tra_rong():
|
|
assert list_conversations_by_project([]) == []
|
|
|
|
|
|
# ---- tìm kiếm vẫn hoạt động khi đã gộp -----------------------------------
|
|
|
|
def test_tim_kiem_ap_cho_moi_thu_muc(hai_project):
|
|
assert len(list_conversations_by_project(hai_project, query="test1")) == 1
|
|
assert len(list_conversations_by_project(hai_project, query="chat cua")) == 2
|
|
assert list_conversations_by_project(hai_project, query="khong-he-co") == []
|
|
|
|
|
|
# ---- một định nghĩa duy nhất cho đường dẫn -------------------------------
|
|
|
|
def test_duong_dan_lich_su_chi_dinh_nghia_mot_cho():
|
|
"""Chuỗi ".cowork_history" từng nằm rải ở ``ui/workspace_tab.py``."""
|
|
from cowork_local.core.projects import HISTORY_SUBDIR
|
|
|
|
repo = Path(__file__).resolve().parents[1]
|
|
src = (repo / "ui" / "workspace_tab.py").read_text(encoding="utf-8")
|
|
|
|
assert HISTORY_SUBDIR == ".cowork_history"
|
|
assert ".cowork_history" not in src, "phải dùng project_history_dir()"
|