Files
cowork-local/tests/test_contracts.py
T
Nam Pham Dinh ThanhandClaude Opus 5 8a9ee5f875 chore(refactor): mục chung của Team Gamma — khung, hợp đồng, cổng CASAN
Sáu việc trong "mục chung" của bản phân công, làm trước khi ba nhánh tính năng
tách ra.

1. Khung 5 tầng theo đúng đường dẫn plan.md: domain/ application/
   infrastructure/ presentation/ platform/ + tests/fakes/ — 38 __init__.py.
   Trước đó là 0 file, mà mọi task của cả ba người đều ghi vào đây.
   Đã kiểm platform/ không che khuất module platform của stdlib.

2. Hợp đồng SecretStore và ConfigRepository (Protocol, chưa cài đặt) + fake
   chạy trong bộ nhớ. Danh sách thuộc tính không bịa: đếm 156 lời gọi
   ctx.config.* trong 29 file rồi lấy những cái dùng thật, xếp theo số lần.
   Cố ý bỏ config.data (36 lời gọi, nhiều nhất) — bê dict thô sang kiến trúc
   mới là bê nguyên vấn đề cũ.

3. tests/test_contracts.py — bài nghiệm thu, không phải test cho vui. Bài
   chính chạy tiến trình riêng và khẳng định dùng fake KHÔNG kéo theo
   cowork_local.config lẫn PySide6; đó là điều kiện để N2 và N3 code ngay hôm
   nay thay vì đợi bản thật ngày 23 và 26/08.

4. scripts/audit_security.py — CASAN Check 1, Gamma chủ trì (hạn 30/08). Viết
   sớm để kiểm liên tục trong lúc chuyển API key, không đợi tới ngày cổng.
   Lần chạy đầu ra 3 báo động giả (secret_in_output là tên quy tắc, api_key="x"
   là dữ liệu test) nên đã siết: ngưỡng độ dài, hằng liệt kê, hình dạng khoá
   i18n, và dấu "# casan: allow" làm lối thoát chuẩn.
   --self-test cắm 4 credential thật + 5 mẫu vô hại để chứng minh nó còn cắn
   được — một máy quét không tìm thấy gì chỉ có giá trị nếu chứng minh được nó
   biết tìm.

5. Ba check CASAN vào CI, chạy mọi PR thay vì dồn tới 30/08. Check 2 và 3
   thuộc Team Hoa và Team Duy, chưa có script — bước CI bỏ qua nếu file chưa
   tồn tại, để thêm cổng không làm đỏ CI của hai team kia.

6. docs/refactor/GammaTeam_decisions.md — hai quyết định chờ nhóm trưởng chốt:
   provider_conf() còn trả api_key hay không (ảnh hưởng 5 nơi, 3 nằm ngoài
   team), và số phận 24 checker UI sẽ vỡ khi file bị dời.

96 test xanh (90 cũ + 6 mới). CASAN Check 1: 0 credential lộ.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-21 20:58:35 +09:00

82 lines
3.2 KiB
Python

"""Hợp đồng của mục chung có thật sự gỡ chốt cho N2 và N3 không.
Đây là bài nghiệm thu, không phải test cho vui: nếu ba bài dưới đây xanh thì
hai nhánh kia code được ngay hôm nay mà không cần chờ ``ConfigRepository`` hay
``KeyringAdapter`` bản thật.
"""
from __future__ import annotations
import subprocess
import sys
from pathlib import Path
from cowork_local.infrastructure.config.config_repository import ConfigRepository
from cowork_local.infrastructure.secrets.secret_store import SecretStore, provider_key
from cowork_local.tests.fakes.fake_config import FakeConfigRepository, FakeSecretStore
REPO_PARENT = Path(__file__).resolve().parents[2]
def test_fake_config_khop_hop_dong():
"""Fake phải cài đủ interface — thiếu một hàm là hai nhánh kia gọi vào sẽ vỡ."""
assert isinstance(FakeConfigRepository(), ConfigRepository)
def test_fake_secret_store_khop_hop_dong():
assert isinstance(FakeSecretStore(), SecretStore)
def test_secret_store_thieu_key_thi_tra_none_chu_khong_nem_loi():
"""Thiếu API key là chuyện thường (người dùng chưa nhập), không phải sự cố."""
store = FakeSecretStore()
assert store.get(provider_key("openai")) is None
assert store.has(provider_key("openai")) is False
store.delete(provider_key("openai")) # xoá cái không có: im lặng
store.set(provider_key("openai"), "sk-test")
assert store.get(provider_key("openai")) == "sk-test"
assert store.has(provider_key("openai")) is True
def test_config_gia_ghi_nhan_save_ma_khong_cham_dia():
cfg = FakeConfigRepository(theme="light")
assert cfg.theme == "light"
cfg.set_theme("dark")
cfg.save()
assert cfg.theme == "dark"
assert cfg.saves == 1
def test_bat_duoc_tool_bi_tat():
cfg = FakeConfigRepository(tools_disabled=["run_command"])
assert cfg.tools_disabled == ["run_command"]
cfg.set_tool_enabled("run_command", True)
assert cfg.tools_disabled == []
cfg.set_tool_enabled("write_file", False)
assert cfg.tools_disabled == ["write_file"]
def test_dung_duoc_fake_ma_khong_hề_nap_config_that():
"""Bài nghiệm thu chính của mục chung.
N2 và N3 phải dựng được màn hình và chạy test của mình mà KHÔNG kéo theo
``cowork_local.config`` — module nặng, đọc đĩa, và đang bị N1 viết lại.
Kiểm bằng tiến trình riêng để không dính module đã nạp sẵn ở test khác.
"""
snippet = (
"import sys\n"
"from cowork_local.tests.fakes.fake_config import "
"FakeConfigRepository, FakeSecretStore\n"
"cfg = FakeConfigRepository(active_provider='openai')\n"
"assert cfg.provider_conf()['model'] == 'gpt-4o-mini'\n"
"assert FakeSecretStore().get('x') is None\n"
"assert 'cowork_local.config' not in sys.modules, "
"'fake keo theo config that -> van con phu thuoc'\n"
"assert 'PySide6' not in sys.modules, 'fake keo theo Qt -> test se cham'\n"
"print('OK')\n"
)
out = subprocess.run([sys.executable, "-c", snippet], cwd=REPO_PARENT,
capture_output=True, text=True, timeout=60)
assert out.returncode == 0, out.stderr
assert "OK" in out.stdout