"""F3+F4 测试:多项目管理 + CLI 工具""" import json import subprocess import sys from pathlib import Path import pytest pytestmark = pytest.mark.unit from src.blackboard.registry import ProjectRegistry from src.blackboard.operations import Blackboard from src.blackboard.models import Task # =================================================================== # F3: 多项目管理 # =================================================================== @pytest.fixture def registry(tmp_path): return ProjectRegistry(tmp_path) class TestProjectRegistry: def test_create_project(self, registry): info = registry.create_project("test-proj", "Test Project", agents=["agent1", "agent2"]) assert info["name"] == "Test Project" assert info["agents"] == ["agent1", "agent2"] assert info["status"] == "active" def test_create_creates_dirs(self, registry): registry.create_project("test-proj", "Test") root = registry.root assert (root / "test-proj" / "config" / "project.yaml").exists() assert (root / "test-proj" / "artifacts").is_dir() assert (root / "test-proj" / "experiences").is_dir() assert (root / "test-proj" / "skills").is_dir() def test_create_duplicate_fails(self, registry): registry.create_project("p1", "P1") with pytest.raises(ValueError, match="already exists"): registry.create_project("p1", "P1 Duplicate") def test_get_project(self, registry): registry.create_project("p1", "P1") info = registry.get_project("p1") assert info is not None assert info["name"] == "P1" def test_get_nonexistent(self, registry): assert registry.get_project("nope") is None def test_list_projects(self, registry): registry.create_project("p1", "P1") registry.create_project("p2", "P2") projects = registry.list_projects() assert len(projects) == 2 assert "p1" in projects assert "p2" in projects def test_archive_project(self, registry): registry.create_project("p1", "P1") assert registry.archive_project("p1") info = registry.get_project("p1") assert info["status"] == "archived" def test_archive_nonexistent(self, registry): assert not registry.archive_project("nope") def test_delete_project(self, registry): registry.create_project("p1", "P1") assert registry.delete_project("p1") # v2.8: 逻辑删除,get_project 返回带 status=deleted 的 dict 而非 None deleted = registry.get_project("p1") assert deleted is not None assert deleted["status"] == "deleted" def test_registry_yaml_persists(self, registry): registry.create_project("p1", "P1") # Reload from disk reg2 = ProjectRegistry(registry.root) assert reg2.get_project("p1") is not None def test_per_project_blackboard(self, tmp_path): """每个项目有独立的黑板 DB""" registry = ProjectRegistry(tmp_path) registry.create_project("proj-a", "A") registry.create_project("proj-b", "B") db_a = tmp_path / "proj-a" / "blackboard.db" db_b = tmp_path / "proj-b" / "blackboard.db" bb_a = Blackboard(db_a) bb_b = Blackboard(db_b) bb_a.create_task(Task(id="t1", title="A Task")) bb_b.create_task(Task(id="t1", title="B Task")) assert bb_a.get_task("t1").title == "A Task" assert bb_b.get_task("t1").title == "B Task"