Files
OpenOPC/tests/test_org_config_roundtrip.py
2026-07-01 17:56:31 +08:00

453 lines
18 KiB
Python

"""Round-trip tests for organization config schema.
Covers:
- save() writes schema_version: 2 org config under company_orgs/
- load() rejects schema_version > 1 with ValueError
- Export YAML round-trips roles/employees bitwise identical
All tests redirect _find_project_root to tmp_path so the real config is untouched.
"""
from __future__ import annotations
import yaml
import pytest
def _write_minimal_corporate(corporate_dir, schema_version: int | None = 1) -> None:
"""Seed a minimal valid corporate yaml in the given directory."""
corporate_dir.mkdir(parents=True, exist_ok=True)
payload: dict = {
"company": {
"name": "Test Co",
"topology": "flat",
"company_profile": "",
"execution_model": "sequential",
"final_decider_role_id": None,
"company_profiles": [],
},
"roles": [],
"employees": [],
"escalation_rules": [],
}
if schema_version is not None:
payload = {"schema_version": schema_version, **payload}
(corporate_dir / "company_corporate_config.yaml").write_text(
yaml.dump(payload, default_flow_style=False, sort_keys=False),
encoding="utf-8",
)
def test_company_org_written_by_save(tmp_path, monkeypatch):
"""OPCConfig.save() must emit the corporate org config under company_orgs/."""
from opc.core import config as cfg_module
from opc.layer2_organization.company_runtime_profiles import get_builtin_roles
monkeypatch.setattr(cfg_module, "_find_project_root", lambda: tmp_path)
cfg = cfg_module.OPCConfig()
cfg.save()
config_dir = tmp_path / ".opc" / "config"
index_path = config_dir / "company_index.yaml"
org_path = config_dir / "company_orgs" / "org_corporate_config.yaml"
assert org_path.exists(), "save() did not create corporate organization config"
assert not index_path.exists(), "save() should not create legacy company_index.yaml"
parsed = yaml.safe_load(org_path.read_text(encoding="utf-8"))
assert parsed.get("schema_version") == 2
assert parsed["organization_id"] == "corporate"
assert parsed["company"]["final_decider_role_id"] == "ceo"
assert [role["id"] for role in parsed["roles"]] == [role.id for role in get_builtin_roles("corporate")]
assert "corporate" in parsed["runtime_policies"]
assert parsed["employees"] == []
assert parsed["talent_templates"] == []
def test_load_rejects_future_schema(tmp_path, monkeypatch):
"""OPCConfig.load() must raise ValueError for schema_version > 1."""
from opc.core import config as cfg_module
monkeypatch.setattr(cfg_module, "_find_project_root", lambda: tmp_path)
corporate_dir = tmp_path / "config"
_write_minimal_corporate(corporate_dir, schema_version=99)
with pytest.raises(ValueError, match="schema_version 99"):
cfg_module.OPCConfig.load(corporate_dir)
def test_load_accepts_current_and_missing_schema_version(tmp_path, monkeypatch):
"""load() must silently accept schema_version: 1 AND legacy files missing the key."""
from opc.core import config as cfg_module
monkeypatch.setattr(cfg_module, "_find_project_root", lambda: tmp_path)
corporate_dir = tmp_path / "config"
# schema_version: 1 — explicit
_write_minimal_corporate(corporate_dir, schema_version=1)
cfg1 = cfg_module.OPCConfig.load(corporate_dir)
assert cfg1 is not None
# missing schema_version — legacy compatible
_write_minimal_corporate(corporate_dir, schema_version=None)
cfg2 = cfg_module.OPCConfig.load(corporate_dir)
assert cfg2 is not None
def test_load_uses_corporate_company_payload_even_when_legacy_index_points_custom(tmp_path, monkeypatch):
"""company_index.yaml must not let a saved custom org replace company mode."""
from opc.core import config as cfg_module
monkeypatch.setattr(cfg_module, "_find_project_root", lambda: tmp_path)
config_dir = tmp_path / ".opc" / "config"
orgs_dir = config_dir / "company_orgs"
orgs_dir.mkdir(parents=True)
corporate_payload = {
"schema_version": 2,
"kind": "opc_org_architecture",
"organization_id": "corporate",
"organization_name": "Corporate",
"company": {
"name": "Corporate",
"company_profile": "corporate",
"execution_model": "actor_runtime",
"company_profiles": ["corporate", "custom"],
},
"roles": [],
"employees": [],
"escalation_rules": [],
}
quantum_payload = {
**corporate_payload,
"organization_id": "quantum_harbor",
"organization_name": "Quantum Harbor",
"company": {
**corporate_payload["company"],
"name": "Quantum Harbor",
"company_profile": "custom",
},
"roles": [{"id": "founder_ceo", "name": "Founder CEO", "responsibility": "Lead"}],
}
(orgs_dir / "org_corporate_config.yaml").write_text(
yaml.dump(corporate_payload, default_flow_style=False, sort_keys=False),
encoding="utf-8",
)
(orgs_dir / "org_quantum_harbor_config.yaml").write_text(
yaml.dump(quantum_payload, default_flow_style=False, sort_keys=False),
encoding="utf-8",
)
(config_dir / "company_index.yaml").write_text(
yaml.dump({"schema_version": 1, "active_organization_id": "quantum_harbor"}),
encoding="utf-8",
)
cfg = cfg_module.OPCConfig.load(config_dir)
assert cfg.org.organization_id == "corporate"
assert cfg.org.company_profile == "corporate"
assert [role.id for role in cfg.org.roles] == []
def test_company_orgs_does_not_use_company_style_fallback_files(tmp_path, monkeypatch):
from opc.layer2_organization.company_runtime_profiles import get_builtin_roles
from opc.core import config as cfg_module
monkeypatch.setattr(cfg_module, "_find_project_root", lambda: tmp_path)
config_dir = tmp_path / ".opc" / "config"
company_orgs_dir = config_dir / "company_orgs"
company_orgs_dir.mkdir(parents=True)
corporate_payload = {
"schema_version": 2,
"kind": "opc_org_architecture",
"organization_id": "corporate",
"organization_name": "Corporate",
"company": {
"name": "Corporate",
"company_profile": "corporate",
"execution_model": "actor_runtime",
"company_profiles": ["corporate", "custom"],
},
"roles": [],
"employees": [],
"escalation_rules": [],
}
corporate_payload["roles"] = [
{"id": "company_style_legacy_role", "name": "Legacy Role", "responsibility": "Should not load"},
]
(company_orgs_dir / "company_corporate_config.yaml").write_text(
yaml.dump(corporate_payload, default_flow_style=False, sort_keys=False),
encoding="utf-8",
)
cfg = cfg_module.OPCConfig.load(config_dir)
assert cfg.org.organization_id == "corporate"
assert cfg.org.company_profile == "corporate"
role_ids = [role.id for role in cfg.org.roles]
assert role_ids == [role.id for role in get_builtin_roles("corporate")]
assert "company_style_legacy_role" not in role_ids
assert (company_orgs_dir / "org_corporate_config.yaml").exists()
def test_custom_org_save_writes_saved_org_storage_not_company_index(tmp_path, monkeypatch):
from opc.core import config as cfg_module
from opc.core.config import RoleConfig
monkeypatch.setattr(cfg_module, "_find_project_root", lambda: tmp_path)
config_dir = tmp_path / ".opc" / "config"
cfg = cfg_module.OPCConfig()
cfg.org.organization_id = "lab"
cfg.org.organization_name = "Lab Org"
cfg.org.organization_config_file = "company_orgs/org_lab_config.yaml"
cfg.org.company_name = "Lab Org"
cfg.org.company_profile = "custom"
cfg.org.roles = [
RoleConfig(id="director", name="Director", responsibility="Own final decisions"),
]
cfg.save(config_dir)
assert (config_dir / "company_orgs" / "org_lab_config.yaml").exists()
assert yaml.safe_load((config_dir / "org_index.yaml").read_text(encoding="utf-8"))["active_organization_id"] == "lab"
assert not (config_dir / "company_index.yaml").exists()
assert not (config_dir / "company_orgs" / "company_lab_config.yaml").exists()
def test_org_payload_filters_runtime_placeholder_employees():
from opc.core.config import EmployeeConfig, OPCConfig, RoleConfig, build_company_org_payload_from_config
cfg = OPCConfig()
cfg.org.roles = [
RoleConfig(id="researcher", name="Researcher", responsibility="Research"),
]
cfg.org.employees = [
EmployeeConfig(employee_id="ava", name="Ava Chen", role_id="researcher"),
EmployeeConfig(
employee_id="researcher-default-employee",
template_id="general-default-employee",
name="Researcher Default Employee",
role_id="researcher",
metadata={
"is_default_employee": True,
"auto_created_for_role": "researcher",
"employee_origin": "system_default",
},
),
EmployeeConfig(
employee_id="researcher-fallback-empty-employee",
template_id="fallback-empty-employee",
name="Researcher Fallback Empty Employee",
role_id="researcher",
metadata={
"is_fallback_employee": True,
"auto_created_for_role": "researcher",
"employee_origin": "recruitment_fallback",
},
),
EmployeeConfig(
employee_id="saved-default",
template_id="general-default-employee",
name="Saved Default",
role_id="researcher",
metadata={
"is_default_employee": True,
"auto_created_for_role": "researcher",
"employee_origin": "system_default",
"persist_to_org": True,
},
),
]
payload = build_company_org_payload_from_config(cfg)
assert payload["employees"] == []
def test_custom_org_save_drops_runtime_placeholder_employees(tmp_path, monkeypatch):
from opc.core import config as cfg_module
from opc.core.config import EmployeeConfig, RoleConfig
monkeypatch.setattr(cfg_module, "_find_project_root", lambda: tmp_path)
config_dir = tmp_path / ".opc" / "config"
cfg = cfg_module.OPCConfig()
cfg.org.organization_id = "lab"
cfg.org.organization_name = "Lab Org"
cfg.org.organization_config_file = "company_orgs/org_lab_config.yaml"
cfg.org.company_name = "Lab Org"
cfg.org.company_profile = "custom"
cfg.org.roles = [
RoleConfig(id="researcher", name="Researcher", responsibility="Research"),
]
cfg.org.employees = [
EmployeeConfig(employee_id="ava", name="Ava Chen", role_id="researcher"),
EmployeeConfig(
employee_id="researcher-default-employee",
template_id="general-default-employee",
name="Researcher Default Employee",
role_id="researcher",
metadata={
"is_default_employee": True,
"auto_created_for_role": "researcher",
"employee_origin": "system_default",
},
),
]
cfg.save(config_dir)
data = yaml.safe_load((config_dir / "company_orgs" / "org_lab_config.yaml").read_text(encoding="utf-8"))
assert data["employees"] == []
registry_path = tmp_path / ".opc" / "company_state" / "lab" / "employees" / "ava.yaml"
assert registry_path.exists()
def test_org_config_load_ignores_runtime_placeholder_employees():
from opc.core.config import OPCConfig
from opc.core.org_config import apply_org_config_payload_to_config
payload = {
"schema_version": 2,
"kind": "company_org",
"organization_id": "lab",
"organization_name": "Lab Org",
"company": {"name": "Lab Org", "company_profile": "custom"},
"roles": [{"id": "researcher", "name": "Researcher", "responsibility": "Research"}],
"employees": [
{"employee_id": "ava", "name": "Ava Chen", "role_id": "researcher"},
{
"employee_id": "researcher-default-employee",
"template_id": "general-default-employee",
"name": "Researcher Default Employee",
"role_id": "researcher",
"metadata": {
"is_default_employee": True,
"auto_created_for_role": "researcher",
"employee_origin": "system_default",
},
},
{
"employee_id": "saved-default",
"template_id": "general-default-employee",
"name": "Saved Default",
"role_id": "researcher",
"metadata": {
"is_default_employee": True,
"auto_created_for_role": "researcher",
"employee_origin": "system_default",
"persist_to_org": True,
},
},
],
"escalation_rules": [],
}
cfg = apply_org_config_payload_to_config(OPCConfig(), payload)
assert [employee.employee_id for employee in cfg.org.employees] == ["ava", "saved-default"]
def test_load_self_heals_legacy_company_org_employees_to_registry(tmp_path, monkeypatch):
from opc.core import config as cfg_module
monkeypatch.setattr(cfg_module, "_find_project_root", lambda: tmp_path)
config_dir = tmp_path / ".opc" / "config"
org_dir = config_dir / "company_orgs"
org_dir.mkdir(parents=True)
org_path = org_dir / "org_corporate_config.yaml"
org_path.write_text(
yaml.dump(
{
"schema_version": 2,
"kind": "opc_org_architecture",
"organization_id": "corporate",
"organization_name": "Corporate",
"company": {
"name": "Corporate",
"company_profile": "corporate",
"execution_model": "actor_runtime",
"company_profiles": ["corporate", "custom"],
},
"roles": [],
"employees": [
{
"employee_id": "ceo-finance-investment-analyst",
"template_id": "finance-investment-analyst",
"name": "Investment Analyst",
"role_id": "ceo",
"description": "Investment research specialist.",
"category": "finance",
}
],
"escalation_rules": [],
"runtime_policies": {},
"talent_templates": [{"id": "legacy-template", "name": "Legacy Template"}],
"teams": [],
"team_runtime": {},
"installed_packages": [],
"role_serial_queue_enabled": True,
},
default_flow_style=False,
sort_keys=False,
),
encoding="utf-8",
)
loaded = cfg_module.OPCConfig.load(config_dir)
healed = yaml.safe_load(org_path.read_text(encoding="utf-8"))
assert healed["employees"] == []
assert healed["talent_templates"] == []
registry_path = tmp_path / ".opc" / "company_state" / "corporate" / "employees" / "finance-investment-analyst.yaml"
assert registry_path.exists()
assert loaded.org.employees[0].employee_id == "finance-investment-analyst"
assert loaded.org.employees[0].metadata["legacy_employee_ids"] == ["ceo-finance-investment-analyst"]
def test_export_yaml_roundtrips_roles_and_employees():
"""The export payload shape must preserve explicit org structure via YAML round-trip."""
from opc.core.config import OPCConfig, build_company_org_payload_from_config
from opc.layer2_organization.company_runtime_profiles import get_builtin_roles
cfg = OPCConfig()
corporate_data = build_company_org_payload_from_config(cfg)
exported = yaml.dump(corporate_data, default_flow_style=False, sort_keys=False)
parsed = yaml.safe_load(exported)
assert parsed["schema_version"] == 2
assert [role["id"] for role in parsed["roles"]] == [role.id for role in get_builtin_roles("corporate")]
assert parsed["company"]["final_decider_role_id"] == "ceo"
assert "corporate" in parsed["runtime_policies"]
assert parsed["employees"] == []
assert parsed["talent_templates"] == []
assert parsed["escalation_rules"] == [e.model_dump() for e in cfg.org.escalation_rules]
def test_custom_org_payload_materializes_effective_policy_when_empty():
from opc.core.config import OPCConfig, RoleConfig, build_company_org_payload_from_config
cfg = OPCConfig()
cfg.org.organization_id = "hkuds"
cfg.org.organization_name = "HKUDS"
cfg.org.company_name = "HKUDS"
cfg.org.company_profile = "custom"
cfg.org.final_decider_role_id = "chao"
cfg.org.roles = [
RoleConfig(id="chao", name="Chao", responsibility="leader", reports_to="owner"),
RoleConfig(id="zongwei", name="Zongwei", responsibility="student", reports_to="chao"),
]
cfg.org.runtime_policies = {}
payload = build_company_org_payload_from_config(
cfg,
organization_id="hkuds",
organization_name="HKUDS",
force_profile="custom",
)
assert [role["id"] for role in payload["roles"]] == ["chao", "zongwei"]
assert payload["company"]["final_decider_role_id"] == "chao"
assert "custom" in payload["runtime_policies"]
assert payload["runtime_policies"]["custom"]["gate_harness"]["decision_mode"] == "hybrid"
assert payload["employees"] == []
assert payload["talent_templates"] == []