Files
cowork-local/tests/test_config_gan_duoc.py
f9f6bc01fd
CI / test (push) Canceled after 0s
Feature/delta team/epic r04 (#7)
## 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>
2026-08-31 05:15:13 +00:00

98 lines
3.9 KiB
Python

"""Mọi chỗ trong repo gán ``config.X = ...`` thì repository phải nhận được.
Bài này sinh ra từ một lỗi thật ngày 26/08.
R02 tráo ``config.py::AppConfig`` bằng ``JsonConfigRepository``. Trước khi
tráo tôi có đối chiếu API: đếm đủ 34/34 thành viên công khai, không thiếu cái
nào, nên kết luận là thay được. Đối chiếu đó **chỉ so tên**, không so việc một
``property`` có setter hay không.
``AppConfig`` cũ là dataclass, ``config.language = "vi"`` chạy bình thường.
Repository để ``language`` là property chỉ đọc, gán vào là ``AttributeError``.
Ba chỗ trong ``app.py`` đang gán như thế: đổi ngôn ngữ, đổi giao diện, đổi
provider trên thanh bên.
Điều làm nó khó thấy: cả ba đều nằm trong slot của Qt, mà Qt **nuốt ngoại lệ
trong slot**. Không có traceback, không có thông báo — người dùng bấm đổi ngôn
ngữ thì không có gì xảy ra. Bộ test đơn vị vẫn 709 xanh; chỉ ``check_nav`` bắt
được vì nó bấm thật vào combo rồi kiểm ngôn ngữ có đổi không.
Nên bài này đi ngược từ mã nguồn: tìm mọi chỗ gán, rồi thử gán thật.
"""
from __future__ import annotations
import copy
import re
import subprocess
from pathlib import Path
import pytest
REPO = Path(__file__).resolve().parent.parent
#: ``config.X = ...`` nhưng không phải ``==``.
GAN = re.compile(r"\bconfig\.([a-z_][a-z_0-9]*)\s*=(?!=)")
#: Không phải thuộc tính cấu hình — là chỗ chứa chính đối tượng config.
BO_QUA = {"data", "config"}
def _cho_gan() -> set[str]:
out = subprocess.run(["git", "ls-files", "*.py"], cwd=REPO,
capture_output=True, text=True, encoding="utf-8",
errors="replace").stdout.split()
ten: set[str] = set()
for f in out:
p = REPO / f
if not p.is_file():
continue
for m in GAN.finditer(p.read_text(encoding="utf-8", errors="replace")):
if m.group(1) not in BO_QUA and not m.group(1).startswith("_"):
ten.add(m.group(1))
return ten
def _repo(tmp_path):
from cowork_local.config import DEFAULT_CONFIG
from cowork_local.infrastructure.config.json_config_repository import (
JsonConfigRepository,
)
return JsonConfigRepository.from_data(copy.deepcopy(DEFAULT_CONFIG),
tmp_path / "config.json")
def test_tim_duoc_cho_gan():
"""Bảo vệ chính bài test: biểu thức tìm kiếm hỏng thì nó lặng lẽ xanh."""
ten = _cho_gan()
assert ten, "không tìm thấy chỗ nào gán config.X — kiểm lại GAN"
assert "language" in ten, f"phải thấy config.language (thấy: {sorted(ten)})"
def test_moi_thuoc_tinh_bi_gan_deu_gan_duoc(tmp_path):
cfg = _repo(tmp_path)
hong = []
for ten in sorted(_cho_gan()):
if not hasattr(type(cfg), ten) and not hasattr(cfg, ten):
continue # thuộc tính của lớp khác, không phải config
cu = getattr(cfg, ten, None)
try:
setattr(cfg, ten, cu)
except AttributeError:
hong.append(ten)
assert not hong, (
"Repository không nhận gán, nhưng trong mã nguồn có chỗ gán:\n "
+ "\n ".join("config.%s = ..." % t for t in hong)
+ "\nQt nuốt AttributeError trong slot, nên chỗ đó sẽ im lặng không "
"làm gì. Thêm @<tên>.setter vào JsonConfigRepository."
)
@pytest.mark.parametrize("ten,gia_tri", [("language", "ja"), ("theme", "light"),
("active_provider", "ollama")])
def test_ba_cho_app_py_dang_gan(tmp_path, ten, gia_tri):
"""Chốt riêng ba cái app.py gán, để bài trên có hỏng thì vẫn còn lưới."""
cfg = _repo(tmp_path)
setattr(cfg, ten, gia_tri)
assert getattr(cfg, ten) == gia_tri