Files
Orchestrator/shared/AI-Orchestrator/tests/test_config.py
ankn a5f8fb49ab Migrate all repos into monorepo context folders
Bahn: aisupport, Analyse-O2C-C2S, awesome-bahn-mcp-servers, beam-mcp,
      Confluence_Bot, db-planet-mcp-server, O2C-Harness, project-audit,
      Projekt-KIQ-HP, teamlandkarte-mcp
Dhive: Jury-Voting
Privat: CV, NoteGraph (NOTE: NoteGraph needs complete redo after consolidation)
Shared: AI-Orchestrator, OrgMyLife, power_skills_and_more
Shared/references: symphony (read-only)

Bahn repos remain available as independent remotes - this monorepo
pulls them in via subtree, the originals are untouched.
2026-06-30 20:39:52 +02:00

139 lines
4.8 KiB
Python

"""Tests for configuration layer."""
import os
from pathlib import Path
import pytest
from ai_orchestrator.config import (
ServiceConfig,
build_config,
expand_path,
resolve_env_var,
validate_dispatch_config,
)
class TestResolveEnvVar:
"""Tests for environment variable resolution."""
def test_resolves_env_var(self, monkeypatch):
monkeypatch.setenv("MY_TOKEN", "secret123")
assert resolve_env_var("$MY_TOKEN") == "secret123"
def test_missing_env_var_returns_empty(self, monkeypatch):
monkeypatch.delenv("NONEXISTENT_VAR", raising=False)
assert resolve_env_var("$NONEXISTENT_VAR") == ""
def test_literal_value_unchanged(self):
assert resolve_env_var("literal_value") == "literal_value"
def test_non_string_passthrough(self):
assert resolve_env_var(123) == 123 # type: ignore
class TestExpandPath:
"""Tests for path expansion."""
def test_tilde_expansion(self, tmp_path):
result = expand_path("~/workspaces", tmp_path)
assert str(Path.home()) in result
def test_relative_path_resolved(self, tmp_path):
result = expand_path("./workspaces", tmp_path)
assert str(tmp_path) in result
def test_absolute_path_unchanged(self, tmp_path):
if os.name == "nt":
result = expand_path("C:\\workspaces", tmp_path)
assert "C:\\" in result
else:
result = expand_path("/tmp/workspaces", tmp_path)
assert result.startswith("/tmp")
def test_empty_uses_default(self, tmp_path):
result = expand_path("", tmp_path)
assert "symphony_workspaces" in result
class TestBuildConfig:
"""Tests for building typed config from raw dict."""
def test_defaults_applied(self, tmp_path):
config = build_config({}, tmp_path)
assert config.polling.interval_ms == 30000
assert config.agent.max_concurrent_agents == 10
assert config.agent.max_turns == 20
assert config.codex.command == "codex app-server"
assert config.codex.turn_timeout_ms == 3600000
assert config.hooks.timeout_ms == 60000
def test_tracker_config(self, tmp_path, monkeypatch):
monkeypatch.setenv("LINEAR_API_KEY", "test-key")
raw = {
"tracker": {
"kind": "linear",
"project_slug": "my-proj",
"active_states": ["Ready", "Working"],
}
}
config = build_config(raw, tmp_path)
assert config.tracker.kind == "linear"
assert config.tracker.project_slug == "my-proj"
assert config.tracker.api_key == "test-key"
assert config.tracker.active_states == ["Ready", "Working"]
assert config.tracker.endpoint == "https://api.linear.app/graphql"
def test_per_state_concurrency_normalization(self, tmp_path):
raw = {
"agent": {
"max_concurrent_agents_by_state": {
"In Progress": 3,
"Todo": "invalid",
"Review": -1,
}
}
}
config = build_config(raw, tmp_path)
# Only valid positive entries kept, normalized to lowercase
assert config.agent.max_concurrent_agents_by_state == {"in progress": 3}
class TestValidateDispatchConfig:
"""Tests for dispatch config validation."""
def test_valid_config(self, tmp_path, monkeypatch):
monkeypatch.setenv("LINEAR_API_KEY", "test-key")
raw = {
"tracker": {"kind": "linear", "project_slug": "proj"},
}
config = build_config(raw, tmp_path)
errors = validate_dispatch_config(config)
assert errors == []
def test_missing_tracker_kind(self, tmp_path):
config = build_config({}, tmp_path)
errors = validate_dispatch_config(config)
assert any("tracker.kind" in e for e in errors)
def test_unsupported_tracker_kind(self, tmp_path, monkeypatch):
monkeypatch.setenv("LINEAR_API_KEY", "key")
raw = {"tracker": {"kind": "jira", "project_slug": "x"}}
config = build_config(raw, tmp_path)
errors = validate_dispatch_config(config)
assert any("Unsupported" in e for e in errors)
def test_missing_api_key(self, tmp_path, monkeypatch):
monkeypatch.delenv("LINEAR_API_KEY", raising=False)
raw = {"tracker": {"kind": "linear", "project_slug": "x"}}
config = build_config(raw, tmp_path)
errors = validate_dispatch_config(config)
assert any("api_key" in e for e in errors)
def test_missing_project_slug(self, tmp_path, monkeypatch):
monkeypatch.setenv("LINEAR_API_KEY", "key")
raw = {"tracker": {"kind": "linear"}}
config = build_config(raw, tmp_path)
errors = validate_dispatch_config(config)
assert any("project_slug" in e for e in errors)