## Summary epic r04 - begin refactor ## Change Type - [x] Cowork feature - [ ] Bug fix - [ ] Core AI contribution - [ ] Test / hardening - [ ] Performance - [ ] Documentation ## Related Work Cowork Task: Core Repo: http://34.143.229.138/gitea-admin/fsg-ai-core-assets Core AI Issue: Core Task: Related PR: ## Scope What is intentionally included? What is intentionally NOT included? ## Validation - [ ] Unit tests - [ ] Integration tests - [ ] Manual verification - [ ] Regression check Commands / evidence: ## Security Impact Permission / credential / network / customer data impact: ## Compatibility - [ ] No breaking change - [ ] Breaking change documented ## Reviewer Notes Anything Cowork reviewers should pay attention to. --------- Co-authored-by: Anh Tran Nguyen Minh <anhtnm1@fpt.com> Co-authored-by: Huong Le Thi Thien <huongltt35@fpt.com> Co-authored-by: Nam Pham Dinh Thanh <nampdt@fpt.com> Co-authored-by: Vu Dam Tuan <vudt15@fpt.com> Co-authored-by: Hiep Ha Van <hiephv3@fpt.com> Co-authored-by: Lam Hoang Van <lamhv7@fpt.com> Reviewed-on: #7 Co-authored-by: Duy Le Huu <duylh19@fpt.com>
This commit was merged in pull request #7.
This commit is contained in:
@@ -0,0 +1,463 @@
|
||||
"""Characterization test cho ``StepConfigPanel`` (``ui/co4e_config_panel.py``,
|
||||
dong 133-528) — KHONG bao gom ``_SectionHeader``/``_add_section`` (khung UI
|
||||
chung, khong co hanh vi nghiep vu rieng dang characterize).
|
||||
|
||||
VI SAO GHI LAI CHU KHONG PHAN XET: day la luoi an toan cho dot tach
|
||||
``StepConfigPanel`` ra khoi ``ui/co4e_config_panel.py`` sang cac module con
|
||||
duoi ``presentation/co4e/`` (vi du ``node_property_panel.py``,
|
||||
``node_property_actions_mixin.py``, ``step_config_section.py`` — xem
|
||||
``docs/architecture/co4e-split-map-node-property.md``). Moi ``assert`` duoi
|
||||
day duoc chot lai tu gia tri THAT in ra khi chay code that (quy trinh nguoc:
|
||||
chay truoc, in ra, dan vao assert) — khong phai gia tri "nen" la gi theo suy
|
||||
doan.
|
||||
|
||||
VI SAO CAN TIEN TRINH CON + CO LAP HOME: ban than ``StepConfigPanel.__init__``/
|
||||
``load_step`` KHONG cham dia/mang, nhung ``_available_agent_names()`` (goi tu
|
||||
``_add_subagent``/``_edit_subagent``) doc THAT tu
|
||||
``core.co4e.list_custom_agents()`` duoi ``CONFIG_DIR/co4e/agents`` — mot
|
||||
hang so module tinh MOT LAN tu ``Path.home()`` luc import. Vi vay HOME/
|
||||
USERPROFILE phai duoc tro sang thu muc tam TRUOC BAT KY import
|
||||
``cowork_local.*`` nao, trong mot tien trinh con sach (patch thuoc tinh sau
|
||||
khi import la khong du) — dung khuon ``tools/capture_screens.py::_isolate_home``
|
||||
va ``tests/characterization/test_co4e_agent_panel.py``. Tien trinh con rieng
|
||||
cung tranh xung dot QApplication singleton neu mot tien trinh pytest khac da
|
||||
tao QApplication trong cung luot chay. ``QT_QPA_PLATFORM=offscreen`` duoc dat
|
||||
TRUOC khi import PySide6 (dung khuon ``tools/check_co4e.py`` dong 22+40-42);
|
||||
script con assert ngay ``str(CONFIG_DIR).startswith(sandbox)`` truoc khi goi
|
||||
bat ky ham co4e nao (giong ``tools/check_co4e.py:47``) — khong co lap la doc
|
||||
(va co the ghi) du lieu that cua nguoi dung qua ``list_custom_agents()``/
|
||||
``save_custom_agent()``.
|
||||
|
||||
KHONG goi provider AI that / spawn thread that: ``_ai_draft()`` va
|
||||
``_load_models()`` (dung ``AgentWorker``/``QThread`` that) KHONG duoc goi o
|
||||
bat ky case nao trong file nay — hai ham do nam NGOAI pham vi duoc giao
|
||||
(139-462) va can duoc characterize rieng, tach biet, voi worker/thread duoc
|
||||
gia lap chu khong start that.
|
||||
|
||||
QUIRK DA DONG DINH (xem case tuong ung ben duoi):
|
||||
* ``gen_btn``/``load_models_btn`` duoc set ``setEnabled(ctx is not None)``
|
||||
ngay trong ``__init__`` (dong 181, 208), NHUNG dong 313 goi
|
||||
``self.setEnabled(False)`` cho CA PANEL o cuoi ``__init__`` — vi Qt tinh
|
||||
``isEnabled()`` hieu qua tu ca chuoi ancestor, ca hai nut deu tra ve
|
||||
``False`` NGAY SAU KHI DUNG XONG bat ke ``ctx`` la gi, cho toi khi
|
||||
``load_step()`` (hoac ``setEnabled(True)`` truc tiep) bat lai ca panel.
|
||||
Doc rieng dong 181/208 se de tuong "ctx=None thi nut luon tat, ctx khac
|
||||
None thi nut luon bat" — sai, ca hai deu tat cho den khi co step duoc nap.
|
||||
* ``rounds_spin`` = ``QSpinBox(range=1..5)`` nhung code goi
|
||||
``setValue(max(1, step.max_verify_rounds))`` (dong 330) — voi
|
||||
``max_verify_rounds=10``, ``max(1, 10) == 10`` nhung ``QSpinBox`` tu kep
|
||||
ve tran cua no nen gia tri hien thi la ``5``, khong phai ``10``. Doc code
|
||||
ma khong chay se tuong gia tri duoc giu nguyen.
|
||||
* ``perm_combo`` voi ``step.permission_preset`` khong nam trong
|
||||
``PERMISSION_PRESETS`` (nhanh fallback cua ``findData`` tra ve -1) roi ve
|
||||
index 0 == ``"inherit"`` — im lang, khong bao loi.
|
||||
* ``load_step`` chi dung skill_names de dung skills_list; mot skill trong
|
||||
``step.skills`` khong con trong ``skill_names`` (skill da bi xoa khoi
|
||||
registry) don gian BIEN MAT khoi checklist — va khi ``_on_edit`` chay lan
|
||||
ke tiep (do BAT KY thay doi field nao khac, khong can dung vao
|
||||
skills_list), ``s.skills`` duoc GHI DE lai chi bang cac item dang hien
|
||||
trong checklist, nen ten skill "mo coi" do bi RUNG VINH VIEN khoi
|
||||
``step.skills`` — mot edit khong lien quan (vi du sua role) am tham xoa
|
||||
du lieu skill cu.
|
||||
* ``variant != "parallel"``: ``_parallel_card`` bi an VA ``sub_list.clear()``
|
||||
duoc goi, nhung ``step.sub_agents`` KHONG bi dung vao neu ``is_par`` False
|
||||
— du liệu van con trong step, chi khong hien tren UI.
|
||||
* ``_add_subagent``: khi ``_available_agent_names()`` tra ve danh sach RONG,
|
||||
code chuyen sang ``QInputDialog.getText`` thay vi ``getItem`` (dong
|
||||
401-402) — nhanh fallback nay chi cham toi khi khong con agent nao (builtin
|
||||
+ custom) de chon, hiem khi xay ra tren du lieu that nhung van la mot
|
||||
nhanh code song.
|
||||
* ``_edit_subagent``: neu agent hien tai cua sub-agent khong con trong danh
|
||||
sach ten kha dung (``cur not in names``), dialog mo tai index 0 thay vi
|
||||
bao loi hay giu nguyen lua chon cu.
|
||||
|
||||
VONG DOI: day la gian giao cho dot tach ``StepConfigPanel`` sang
|
||||
``presentation/co4e/`` (xem ``docs/architecture/co4e-split-map-node-property.md``
|
||||
cho ke hoach tach cu the). Sau khi tach xong thanh cac lop/mixin voi hop dong
|
||||
ro rang, cac case o day nen duoc viet lai thanh test dac ta cho tung phan
|
||||
(khong can dung ca ``QApplication``/tien trinh con neu phan tach ra la logic
|
||||
thuan). Ba quirk "panel disabled de len enable cua nut", "orphan skill bi xoa
|
||||
qua mot edit khong lien quan" va "sub_agents khong dong bo voi sub_list khi
|
||||
doi variant" dang mo issue hoi y kien san pham truoc khi ai do "don" chung
|
||||
trong luc tach — dac biet quirk orphan-skill, vi day la mot dang mat du lieu
|
||||
tham lang de bi coi la bug can sua ngay khi gap lai, nhung sua no thay doi
|
||||
hanh vi luu tru hien co ma khong ai ro co ai dang phu thuoc vao khong.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent.parent
|
||||
REPO_PARENT = REPO_ROOT.parent
|
||||
|
||||
_SCRIPT = r"""
|
||||
import sys
|
||||
sys.path.insert(0, REPO_PARENT_PLACEHOLDER)
|
||||
|
||||
from pathlib import Path as _PP
|
||||
from PySide6.QtWidgets import QApplication, QInputDialog, QFileDialog, QListWidgetItem
|
||||
from PySide6.QtCore import Qt
|
||||
|
||||
from cowork_local.config import CONFIG_DIR
|
||||
|
||||
sandbox = sys.argv[1]
|
||||
assert str(CONFIG_DIR).startswith(str(_PP(sandbox))), "khong co lap: CONFIG_DIR=" + str(CONFIG_DIR)
|
||||
|
||||
app = QApplication([])
|
||||
|
||||
from cowork_local.ui.co4e_config_panel import StepConfigPanel
|
||||
from cowork_local.core.co4e import Step, SubAgent, PERMISSION_PRESETS
|
||||
from cowork_local.core import co4e as co4e_mod
|
||||
from cowork_local.core.co4e_builtins import BUILTIN_AGENTS
|
||||
|
||||
# ---- __init__ (139-313) ----------------------------------------------------
|
||||
p = StepConfigPanel()
|
||||
assert p.gen_btn.isEnabled() is False
|
||||
assert p.load_models_btn.isEnabled() is False
|
||||
assert p.isEnabled() is False, "panel bat dau bi setEnabled(False) o dong 313"
|
||||
assert p.perm_combo.count() == len(PERMISSION_PRESETS) == 4
|
||||
assert [p.perm_combo.itemData(i) for i in range(4)] == list(PERMISSION_PRESETS)
|
||||
assert (p.rounds_spin.minimum(), p.rounds_spin.maximum()) == (1, 5)
|
||||
print("CASE_INIT_DEFAULT_CTX_NONE_OK")
|
||||
|
||||
# quirk: gen_btn/load_models_btn duoc set theo ctx, nhung setEnabled(False)
|
||||
# cho CA PANEL o cuoi __init__ de len tren ca hai -> ca hai deu False cho den
|
||||
# khi mot cai gi do bat lai ca panel (vd load_step() goi setEnabled(True)).
|
||||
p_ctx = StepConfigPanel(ctx=object())
|
||||
assert p_ctx.gen_btn.isEnabled() is False, "quirk: panel disabled de len len ctx!=None"
|
||||
assert p_ctx.load_models_btn.isEnabled() is False
|
||||
p_ctx.setEnabled(True) # mo phong dieu load_step() lam
|
||||
assert p_ctx.gen_btn.isEnabled() is True
|
||||
assert p_ctx.load_models_btn.isEnabled() is True
|
||||
|
||||
p_none = StepConfigPanel(ctx=None)
|
||||
p_none.setEnabled(True)
|
||||
assert p_none.gen_btn.isEnabled() is False, "ctx=None -> nut van tat sau khi panel duoc bat"
|
||||
assert p_none.load_models_btn.isEnabled() is False
|
||||
print("CASE_INIT_CTX_ENABLE_QUIRK_OK")
|
||||
|
||||
p.show()
|
||||
app.processEvents()
|
||||
assert p._parallel_card.isVisible() is True, "form4/_parallel_card khong tu an luc dung"
|
||||
print("CASE_INIT_PARALLEL_CARD_DEFAULT_VISIBLE_OK")
|
||||
|
||||
# ---- load_step (316-354): step "step" thuong -------------------------------
|
||||
step = Step(
|
||||
variant="step", label="My Step", role="worker", icon="file",
|
||||
instructions="Do X", context="bg info", model="gpt-4",
|
||||
permission_preset="standard", self_verify=False, max_verify_rounds=3,
|
||||
skills=["Test Skill", "OrphanSkill"], attachments=["C:/foo/bar/baz.txt", "note.md"],
|
||||
sub_agents=[SubAgent(agent="Ghost")],
|
||||
)
|
||||
p.load_step("n1", step, ["Test Skill", "Other Skill"])
|
||||
assert p.label_edit.text() == "My Step"
|
||||
assert p.role_edit.text() == "worker"
|
||||
assert p.icon_edit.currentText() == "file"
|
||||
assert p.instructions_edit.toPlainText() == "Do X"
|
||||
assert p.context_edit.toPlainText() == "bg info"
|
||||
assert p.model_combo.currentText() == "gpt-4"
|
||||
assert (p.perm_combo.currentIndex(), p.perm_combo.currentData()) == (2, "standard")
|
||||
assert p.verify_chk.isChecked() is False
|
||||
assert p.rounds_spin.value() == 3
|
||||
assert p.skills_list.count() == 2, "chi 2 muc trong skill_names duoc ve, OrphanSkill khong co hang"
|
||||
assert [p.skills_list.item(i).text() for i in range(2)] == ["Test Skill", "Other Skill"]
|
||||
assert p.skills_list.item(0).checkState() == Qt.Checked
|
||||
assert p.skills_list.item(1).checkState() == Qt.Unchecked
|
||||
assert p.attach_list.count() == 2
|
||||
assert p.attach_list.item(0).text() == "baz.txt"
|
||||
assert p.attach_list.item(0).toolTip() == "C:/foo/bar/baz.txt"
|
||||
assert p.attach_list.item(1).text() == "note.md"
|
||||
app.processEvents()
|
||||
assert p._parallel_card.isVisible() is False, "variant='step' -> card an"
|
||||
assert p.sub_list.count() == 0, "quirk: sub_agents khong rong nhung khong dong bo vao UI khi khong phai parallel"
|
||||
assert step.sub_agents == [SubAgent(agent="Ghost")], "du lieu step khong bi dong cham, chi UI khong ve"
|
||||
print("CASE_LOAD_STEP_BASIC_OK")
|
||||
|
||||
# quirk: rounds_spin.setValue(max(1, n)) nhung QSpinBox tu kep tran o 5.
|
||||
p.load_step("n0", Step(max_verify_rounds=0), [])
|
||||
assert p.rounds_spin.value() == 1
|
||||
p.load_step("nneg", Step(max_verify_rounds=-5), [])
|
||||
assert p.rounds_spin.value() == 1
|
||||
p.load_step("n10", Step(max_verify_rounds=10), [])
|
||||
assert p.rounds_spin.value() == 5, "quirk: max(1,10)=10 nhung QSpinBox kep ve tran 5"
|
||||
print("CASE_LOAD_STEP_ROUNDS_CLAMP_QUIRK_OK")
|
||||
|
||||
# quirk: permission_preset la khoa khong ton tai -> fallback ve index 0 (inherit)
|
||||
p.load_step("nbad", Step(permission_preset="does-not-exist"), [])
|
||||
assert (p.perm_combo.currentIndex(), p.perm_combo.currentData()) == (0, "inherit")
|
||||
print("CASE_LOAD_STEP_UNKNOWN_PRESET_FALLBACK_OK")
|
||||
|
||||
# variant="parallel" -> card hien, sub_list duoc ve tu sub_agents
|
||||
steppar = Step(variant="parallel", sub_agents=[SubAgent(agent="A1"), SubAgent(agent="A2")])
|
||||
p.load_step("npar", steppar, [])
|
||||
app.processEvents()
|
||||
assert p._parallel_card.isVisible() is True
|
||||
assert [p.sub_list.item(i).text() for i in range(p.sub_list.count())] == ["A1", "A2"]
|
||||
print("CASE_LOAD_STEP_PARALLEL_OK")
|
||||
|
||||
# rong: skills/attachments rong -> list rong, khong loi
|
||||
p.load_step("nempty", Step(), [])
|
||||
assert p.skills_list.count() == 0
|
||||
assert p.attach_list.count() == 0
|
||||
print("CASE_LOAD_STEP_EMPTY_OK")
|
||||
|
||||
# ---- clear_step (356-359) ---------------------------------------------------
|
||||
p.clear_step()
|
||||
assert p._step is None
|
||||
assert p._node_id == ""
|
||||
assert p.isEnabled() is False
|
||||
print("CASE_CLEAR_STEP_OK")
|
||||
|
||||
# _on_edit voi _step None -> khong loi, khong lam gi
|
||||
p._on_edit()
|
||||
print("CASE_ON_EDIT_NO_STEP_NOOP_OK")
|
||||
|
||||
# ---- _on_edit (362-378): guard _loading, fallback role, quirk orphan-skill --
|
||||
p.load_step("npar2", step, ["Test Skill", "Other Skill"])
|
||||
assert step.skills == ["Test Skill", "OrphanSkill"], "chua edit gi thi step chua bi dong den"
|
||||
p.role_edit.setText("") # -> _on_edit tu dong chay qua signal that, khong mock
|
||||
assert step.role == "AGENT", "role rong -> fallback 'AGENT' (dong 367)"
|
||||
assert step.skills == ["Test Skill"], (
|
||||
"quirk: mot edit KHONG LIEN QUAN (sua role) cung ghi de s.skills bang "
|
||||
"danh sach dang checked trong UI -> OrphanSkill bi rung vinh vien"
|
||||
)
|
||||
print("CASE_ON_EDIT_ROLE_FALLBACK_AND_ORPHAN_SKILL_QUIRK_OK")
|
||||
|
||||
p._loading = True
|
||||
step.role = "UNTOUCHED"
|
||||
p.role_edit.setText("Something Else")
|
||||
assert step.role == "UNTOUCHED", "guard _loading chan _on_edit khong ghi lai step"
|
||||
p._loading = False
|
||||
print("CASE_ON_EDIT_LOADING_GUARD_OK")
|
||||
|
||||
p.role_edit.setText("scout")
|
||||
assert step.role == "SCOUT", "role duoc upper() hoa (dong 367)"
|
||||
print("CASE_ON_EDIT_ROLE_UPPERCASE_OK")
|
||||
|
||||
# ---- _available_agent_names (380-390, static) ------------------------------
|
||||
names0 = StepConfigPanel._available_agent_names()
|
||||
assert names0 == [a.name for a in BUILTIN_AGENTS], "khong co custom agent -> chi builtin, dung thu tu"
|
||||
assert names0[0] == "Business Analyst"
|
||||
assert len(names0) == len(BUILTIN_AGENTS) == 19
|
||||
print("CASE_AVAILABLE_NAMES_NO_CUSTOM_OK")
|
||||
|
||||
custom = co4e_mod.new_custom_agent("Zed Custom")
|
||||
co4e_mod.save_custom_agent(custom)
|
||||
names1 = StepConfigPanel._available_agent_names()
|
||||
assert names1[0] == "Zed Custom", "custom agent dung TRUOC builtin"
|
||||
assert len(names1) == len(BUILTIN_AGENTS) + 1
|
||||
print("CASE_AVAILABLE_NAMES_ONE_CUSTOM_OK")
|
||||
|
||||
# quirk: custom trung ten voi mot builtin -> chi giu 1 lan (dong 389: "not in names")
|
||||
dup = co4e_mod.new_custom_agent(BUILTIN_AGENTS[0].name)
|
||||
co4e_mod.save_custom_agent(dup)
|
||||
names2 = StepConfigPanel._available_agent_names()
|
||||
assert names2.count(BUILTIN_AGENTS[0].name) == 1, "trung ten voi builtin bi loai bo, khong nhan doi"
|
||||
assert names2[0] == "Zed Custom"
|
||||
print("CASE_AVAILABLE_NAMES_DEDUPE_WITH_BUILTIN_OK")
|
||||
|
||||
# ---- _add_subagent (392-408) ------------------------------------------------
|
||||
pstep = Step(variant="parallel")
|
||||
p.load_step("nsub", pstep, [])
|
||||
p._step = None
|
||||
before_n = p.sub_list.count()
|
||||
p._add_subagent()
|
||||
assert p.sub_list.count() == before_n, "_step None -> _add_subagent la no-op"
|
||||
print("CASE_ADD_SUBAGENT_NO_STEP_NOOP_OK")
|
||||
|
||||
p._step = pstep
|
||||
orig_getItem = QInputDialog.getItem
|
||||
orig_getText = QInputDialog.getText
|
||||
changed_n = {"n": 0}
|
||||
p.changed.connect(lambda: changed_n.__setitem__("n", changed_n["n"] + 1))
|
||||
|
||||
QInputDialog.getItem = staticmethod(lambda *a, **k: ("Picked Agent", True))
|
||||
p._add_subagent()
|
||||
assert [s.agent for s in pstep.sub_agents] == ["Picked Agent"]
|
||||
assert [p.sub_list.item(i).text() for i in range(p.sub_list.count())] == ["Picked Agent"]
|
||||
assert changed_n["n"] == 1
|
||||
print("CASE_ADD_SUBAGENT_PICKED_OK")
|
||||
|
||||
QInputDialog.getItem = staticmethod(lambda *a, **k: ("", True))
|
||||
p._add_subagent()
|
||||
assert [s.agent for s in pstep.sub_agents] == ["Picked Agent"], "ten rong (sau strip) -> khong them"
|
||||
assert changed_n["n"] == 1, "khong them thi khong emit changed"
|
||||
print("CASE_ADD_SUBAGENT_EMPTY_NAME_NOOP_OK")
|
||||
|
||||
QInputDialog.getItem = staticmethod(lambda *a, **k: ("Should Not Add", False))
|
||||
p._add_subagent()
|
||||
assert [s.agent for s in pstep.sub_agents] == ["Picked Agent"], "ok=False -> khong them"
|
||||
print("CASE_ADD_SUBAGENT_CANCELLED_NOOP_OK")
|
||||
|
||||
# quirk: khi _available_agent_names() rong -> dung getText thay vi getItem (dong 401-402)
|
||||
# GHI CHU DOT TACH mixin (khong doi hanh vi, chi doi CACH patch/restore trong
|
||||
# test): sau khi StepConfigPanel._available_agent_names duoc dua vao mixin
|
||||
# rieng (_StepConfigActionsMixin), no khong con nam trong
|
||||
# StepConfigPanel.__dict__ nua (ma nam trong __dict__ cua mixin, StepConfigPanel
|
||||
# chi ke thua qua MRO) nen "StepConfigPanel.__dict__['_available_agent_names']"
|
||||
# nem KeyError. Gan de ghi de truc tiep len StepConfigPanel (nhu dong duoi) van
|
||||
# shadow dung nhu truoc; khi xong chi can `del` thuoc tinh do khoi
|
||||
# StepConfigPanel de no roi ve lai dung method ke thua tu mixin - tuong duong
|
||||
# hanh vi voi cach "luu roi gan lai" cu, khong lam yeu di assert nao.
|
||||
StepConfigPanel._available_agent_names = staticmethod(lambda: [])
|
||||
QInputDialog.getText = staticmethod(lambda *a, **k: ("Typed Agent", True))
|
||||
p._add_subagent()
|
||||
assert [s.agent for s in pstep.sub_agents] == ["Picked Agent", "Typed Agent"]
|
||||
del StepConfigPanel._available_agent_names
|
||||
QInputDialog.getItem = orig_getItem
|
||||
QInputDialog.getText = orig_getText
|
||||
print("CASE_ADD_SUBAGENT_EMPTY_NAMES_USES_GETTEXT_QUIRK_OK")
|
||||
|
||||
# ---- _edit_subagent (410-428) -----------------------------------------------
|
||||
pstep2 = Step(variant="parallel", sub_agents=[SubAgent(agent="Unknown Agent XYZ")])
|
||||
p.load_step("nedit", pstep2, [])
|
||||
captured = {}
|
||||
|
||||
|
||||
def _fake_get_item(*a, **k):
|
||||
captured["items"] = a[3]
|
||||
captured["current_index"] = a[4]
|
||||
return ("Renamed", True)
|
||||
|
||||
|
||||
QInputDialog.getItem = staticmethod(_fake_get_item)
|
||||
item0 = p.sub_list.item(0)
|
||||
p._edit_subagent(item0)
|
||||
assert captured["current_index"] == 0, "quirk: cur khong nam trong names -> mo dialog tai index 0"
|
||||
assert pstep2.sub_agents[0].agent == "Renamed"
|
||||
assert item0.text() == "Renamed"
|
||||
print("CASE_EDIT_SUBAGENT_UNKNOWN_CUR_STARTS_AT_0_OK")
|
||||
|
||||
foreign_item = QListWidgetItem("not in list")
|
||||
snapshot = dict(captured)
|
||||
p._edit_subagent(foreign_item)
|
||||
assert captured == snapshot, "item khong thuoc sub_list (row=-1) -> no-op"
|
||||
QInputDialog.getItem = orig_getItem
|
||||
print("CASE_EDIT_SUBAGENT_FOREIGN_ITEM_NOOP_OK")
|
||||
|
||||
# ---- _del_subagent (430-437) -------------------------------------------------
|
||||
pstep3 = Step(variant="parallel", sub_agents=[SubAgent(agent="A"), SubAgent(agent="B")])
|
||||
p.load_step("ndel", pstep3, [])
|
||||
p.sub_list.setCurrentRow(-1)
|
||||
p._del_subagent()
|
||||
assert [s.agent for s in pstep3.sub_agents] == ["A", "B"], "khong chon dong nao -> no-op"
|
||||
p.sub_list.setCurrentRow(0)
|
||||
p._del_subagent()
|
||||
assert [s.agent for s in pstep3.sub_agents] == ["B"]
|
||||
assert [p.sub_list.item(i).text() for i in range(p.sub_list.count())] == ["B"]
|
||||
print("CASE_DEL_SUBAGENT_OK")
|
||||
|
||||
# ---- _add_attachment (439-453) -----------------------------------------------
|
||||
astep = Step(attachments=["already/here.txt"])
|
||||
p.load_step("natt", astep, [])
|
||||
orig_getOpenFileNames = QFileDialog.getOpenFileNames
|
||||
QFileDialog.getOpenFileNames = staticmethod(
|
||||
lambda *a, **k: (["already/here.txt", "new/one.txt", ""], "")
|
||||
)
|
||||
att_changed = {"n": 0}
|
||||
p.changed.connect(lambda: att_changed.__setitem__("n", att_changed["n"] + 1))
|
||||
p._add_attachment()
|
||||
assert astep.attachments == ["already/here.txt", "new/one.txt"], (
|
||||
"duplicate bi loai (dong 447), chuoi rong bi loai boi 'if f and ...'"
|
||||
)
|
||||
assert p.attach_list.count() == 2
|
||||
print("CASE_ADD_ATTACHMENT_DEDUPE_AND_SKIP_EMPTY_OK")
|
||||
|
||||
before_att_changed = att_changed["n"]
|
||||
QFileDialog.getOpenFileNames = staticmethod(lambda *a, **k: ([], ""))
|
||||
p._add_attachment()
|
||||
assert att_changed["n"] == before_att_changed, "files rong -> khong emit changed (dong 452-453)"
|
||||
QFileDialog.getOpenFileNames = orig_getOpenFileNames
|
||||
print("CASE_ADD_ATTACHMENT_EMPTY_FILES_NO_CHANGED_OK")
|
||||
|
||||
p._step = None
|
||||
before_att_count = p.attach_list.count()
|
||||
p._add_attachment()
|
||||
assert p.attach_list.count() == before_att_count, "_step None -> no-op"
|
||||
p._step = astep
|
||||
print("CASE_ADD_ATTACHMENT_NO_STEP_NOOP_OK")
|
||||
|
||||
# ---- _del_attachment (455-462) -----------------------------------------------
|
||||
p.attach_list.setCurrentRow(-1)
|
||||
p._del_attachment()
|
||||
assert astep.attachments == ["already/here.txt", "new/one.txt"], "khong chon dong -> no-op"
|
||||
p.attach_list.setCurrentRow(0)
|
||||
p._del_attachment()
|
||||
assert astep.attachments == ["new/one.txt"]
|
||||
assert p.attach_list.count() == 1
|
||||
print("CASE_DEL_ATTACHMENT_OK")
|
||||
|
||||
print("ALL_OK")
|
||||
"""
|
||||
|
||||
|
||||
def _run_isolated(sandbox: Path) -> subprocess.CompletedProcess:
|
||||
env = dict(os.environ)
|
||||
env["HOME"] = str(sandbox)
|
||||
env["USERPROFILE"] = str(sandbox)
|
||||
env["QT_QPA_PLATFORM"] = "offscreen"
|
||||
env["PYTHONIOENCODING"] = "utf-8"
|
||||
env.pop("HOMEDRIVE", None)
|
||||
env.pop("HOMEPATH", None)
|
||||
|
||||
script = _SCRIPT.replace("REPO_PARENT_PLACEHOLDER", repr(str(REPO_PARENT)))
|
||||
return subprocess.run(
|
||||
[sys.executable, "-c", script, str(sandbox)],
|
||||
cwd=str(REPO_ROOT),
|
||||
env=env,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=60,
|
||||
)
|
||||
|
||||
|
||||
def test_step_config_panel_hanh_vi_hien_tai(tmp_path):
|
||||
sandbox = tmp_path / "home"
|
||||
sandbox.mkdir()
|
||||
|
||||
result = _run_isolated(sandbox)
|
||||
|
||||
assert result.returncode == 0, (
|
||||
f"characterization script that bai (exit {result.returncode}):\n"
|
||||
f"--- stdout ---\n{result.stdout}\n--- stderr ---\n{result.stderr}"
|
||||
)
|
||||
for marker in (
|
||||
"CASE_INIT_DEFAULT_CTX_NONE_OK",
|
||||
"CASE_INIT_CTX_ENABLE_QUIRK_OK",
|
||||
"CASE_INIT_PARALLEL_CARD_DEFAULT_VISIBLE_OK",
|
||||
"CASE_LOAD_STEP_BASIC_OK",
|
||||
"CASE_LOAD_STEP_ROUNDS_CLAMP_QUIRK_OK",
|
||||
"CASE_LOAD_STEP_UNKNOWN_PRESET_FALLBACK_OK",
|
||||
"CASE_LOAD_STEP_PARALLEL_OK",
|
||||
"CASE_LOAD_STEP_EMPTY_OK",
|
||||
"CASE_CLEAR_STEP_OK",
|
||||
"CASE_ON_EDIT_NO_STEP_NOOP_OK",
|
||||
"CASE_ON_EDIT_ROLE_FALLBACK_AND_ORPHAN_SKILL_QUIRK_OK",
|
||||
"CASE_ON_EDIT_LOADING_GUARD_OK",
|
||||
"CASE_ON_EDIT_ROLE_UPPERCASE_OK",
|
||||
"CASE_AVAILABLE_NAMES_NO_CUSTOM_OK",
|
||||
"CASE_AVAILABLE_NAMES_ONE_CUSTOM_OK",
|
||||
"CASE_AVAILABLE_NAMES_DEDUPE_WITH_BUILTIN_OK",
|
||||
"CASE_ADD_SUBAGENT_NO_STEP_NOOP_OK",
|
||||
"CASE_ADD_SUBAGENT_PICKED_OK",
|
||||
"CASE_ADD_SUBAGENT_EMPTY_NAME_NOOP_OK",
|
||||
"CASE_ADD_SUBAGENT_CANCELLED_NOOP_OK",
|
||||
"CASE_ADD_SUBAGENT_EMPTY_NAMES_USES_GETTEXT_QUIRK_OK",
|
||||
"CASE_EDIT_SUBAGENT_UNKNOWN_CUR_STARTS_AT_0_OK",
|
||||
"CASE_EDIT_SUBAGENT_FOREIGN_ITEM_NOOP_OK",
|
||||
"CASE_DEL_SUBAGENT_OK",
|
||||
"CASE_ADD_ATTACHMENT_DEDUPE_AND_SKIP_EMPTY_OK",
|
||||
"CASE_ADD_ATTACHMENT_EMPTY_FILES_NO_CHANGED_OK",
|
||||
"CASE_ADD_ATTACHMENT_NO_STEP_NOOP_OK",
|
||||
"CASE_DEL_ATTACHMENT_OK",
|
||||
"ALL_OK",
|
||||
):
|
||||
assert marker in result.stdout, f"thieu marker {marker}\n{result.stdout}"
|
||||
|
||||
# Sandbox chi duoc dung boi save_custom_agent() cua chinh test (khong co
|
||||
# du lieu nguoi dung that nao bi cham vao) - CONFIG_DIR nam trong sandbox
|
||||
# nhu da assert ngay dau script.
|
||||
assert (sandbox / ".cowork_local" / "co4e").exists()
|
||||
Reference in New Issue
Block a user