Move python implementation under python/ in preparation for the upcoming Go rewrite
This commit is contained in:
435
python/tests/test_new_config.py
Normal file
435
python/tests/test_new_config.py
Normal file
@@ -0,0 +1,435 @@
|
||||
import pytest
|
||||
|
||||
from audita.core.config import (
|
||||
AuditaConfig,
|
||||
ConfigOverrides,
|
||||
DEFAULT_GLOSSARY_CONFIDENCE_THRESHOLD,
|
||||
DEFAULT_GRAMMAR_CONFIDENCE_THRESHOLD,
|
||||
DEFAULT_HOMOPHONES_CONFIDENCE_THRESHOLD,
|
||||
DEFAULT_LLM_CONCURRENCY,
|
||||
DEFAULT_LLM_TIMEOUT_SECONDS,
|
||||
DEFAULT_MAX_SECTION_TOKENS,
|
||||
DEFAULT_MIN_SECTION_TOKENS,
|
||||
DEFAULT_NORMALIZE_MAX_SEGMENT_GAP,
|
||||
DEFAULT_SPOKEN_WORD_CONFIDENCE_THRESHOLD,
|
||||
DEFAULT_VALIDATION_MAX_PROMPT_TOKENS,
|
||||
DEFAULT_WORK_DIR_RETENTION,
|
||||
)
|
||||
from audita.core.errors import AuditaConfigError
|
||||
from audita.modules import DEFAULT_MODULE_KEYS
|
||||
|
||||
|
||||
def test_default_config_allows_missing_api_key():
|
||||
config = AuditaConfig.from_sources(env={})
|
||||
|
||||
assert config.api_key is None
|
||||
assert config.llm_concurrency == DEFAULT_LLM_CONCURRENCY
|
||||
assert config.llm_timeout_seconds == DEFAULT_LLM_TIMEOUT_SECONDS
|
||||
assert config.validation_llm_api_key is None
|
||||
assert config.validation_llm_concurrency is None
|
||||
assert config.validation_llm_timeout_seconds is None
|
||||
assert config.validation_model is None
|
||||
assert config.validation_base_url is None
|
||||
assert config.validation_max_retries is None
|
||||
assert config.validation_max_prompt_tokens == DEFAULT_VALIDATION_MAX_PROMPT_TOKENS
|
||||
assert config.target_sections is None
|
||||
assert config.max_section_tokens == DEFAULT_MAX_SECTION_TOKENS
|
||||
assert config.min_section_tokens == DEFAULT_MIN_SECTION_TOKENS
|
||||
assert config.module_keys == DEFAULT_MODULE_KEYS
|
||||
assert config.glossary_confidence_threshold == DEFAULT_GLOSSARY_CONFIDENCE_THRESHOLD
|
||||
assert config.grammar_confidence_threshold == DEFAULT_GRAMMAR_CONFIDENCE_THRESHOLD
|
||||
assert config.homophones_confidence_threshold == DEFAULT_HOMOPHONES_CONFIDENCE_THRESHOLD
|
||||
assert config.spoken_word_confidence_threshold == DEFAULT_SPOKEN_WORD_CONFIDENCE_THRESHOLD
|
||||
assert config.normalize_max_segment_gap == DEFAULT_NORMALIZE_MAX_SEGMENT_GAP
|
||||
assert config.work_dir_retention == DEFAULT_WORK_DIR_RETENTION
|
||||
|
||||
|
||||
def test_cli_overrides_take_precedence():
|
||||
config = AuditaConfig.from_sources(
|
||||
env={"AUDITA_MAX_SECTION_TOKENS": "1000"},
|
||||
overrides=ConfigOverrides(max_section_tokens=2000, min_section_tokens=1000),
|
||||
)
|
||||
|
||||
assert config.max_section_tokens == 2000
|
||||
|
||||
|
||||
def test_min_section_tokens_cli_override_takes_precedence():
|
||||
config = AuditaConfig.from_sources(
|
||||
env={"AUDITA_MIN_SECTION_TOKENS": "2000"},
|
||||
overrides=ConfigOverrides(min_section_tokens=6000),
|
||||
)
|
||||
|
||||
assert config.min_section_tokens == 6000
|
||||
|
||||
|
||||
def test_llm_concurrency_cli_override_takes_precedence():
|
||||
config = AuditaConfig.from_sources(
|
||||
env={"AUDITA_LLM_CONCURRENCY": "2"},
|
||||
overrides=ConfigOverrides(llm_concurrency=4),
|
||||
)
|
||||
|
||||
assert config.llm_concurrency == 4
|
||||
|
||||
|
||||
def test_llm_concurrency_env_is_parsed():
|
||||
config = AuditaConfig.from_sources(env={"AUDITA_LLM_CONCURRENCY": "3"})
|
||||
|
||||
assert config.llm_concurrency == 3
|
||||
|
||||
|
||||
def test_llm_timeout_seconds_cli_override_takes_precedence():
|
||||
config = AuditaConfig.from_sources(
|
||||
env={"AUDITA_LLM_TIMEOUT_SECONDS": "120"},
|
||||
overrides=ConfigOverrides(llm_timeout_seconds=900.0),
|
||||
)
|
||||
|
||||
assert config.llm_timeout_seconds == 900.0
|
||||
|
||||
|
||||
def test_llm_timeout_seconds_env_is_parsed():
|
||||
config = AuditaConfig.from_sources(env={"AUDITA_LLM_TIMEOUT_SECONDS": "120.5"})
|
||||
|
||||
assert config.llm_timeout_seconds == 120.5
|
||||
|
||||
|
||||
def test_validation_llm_concurrency_cli_override_takes_precedence():
|
||||
config = AuditaConfig.from_sources(
|
||||
env={"AUDITA_VALIDATION_LLM_CONCURRENCY": "2"},
|
||||
overrides=ConfigOverrides(validation_llm_concurrency=4),
|
||||
)
|
||||
|
||||
assert config.validation_llm_concurrency == 4
|
||||
|
||||
|
||||
def test_validation_llm_concurrency_env_is_parsed():
|
||||
config = AuditaConfig.from_sources(env={"AUDITA_VALIDATION_LLM_CONCURRENCY": "3"})
|
||||
|
||||
assert config.validation_llm_concurrency == 3
|
||||
|
||||
|
||||
def test_validation_llm_timeout_seconds_cli_override_takes_precedence():
|
||||
config = AuditaConfig.from_sources(
|
||||
env={"AUDITA_VALIDATION_LLM_TIMEOUT_SECONDS": "120"},
|
||||
overrides=ConfigOverrides(validation_llm_timeout_seconds=900.0),
|
||||
)
|
||||
|
||||
assert config.validation_llm_timeout_seconds == 900.0
|
||||
|
||||
|
||||
def test_validation_llm_timeout_seconds_env_is_parsed():
|
||||
config = AuditaConfig.from_sources(env={"AUDITA_VALIDATION_LLM_TIMEOUT_SECONDS": "120.5"})
|
||||
|
||||
assert config.validation_llm_timeout_seconds == 120.5
|
||||
|
||||
|
||||
def test_validation_model_env_is_parsed():
|
||||
config = AuditaConfig.from_sources(env={"AUDITA_VALIDATION_MODEL": "validator-model"})
|
||||
|
||||
assert config.validation_model == "validator-model"
|
||||
|
||||
|
||||
def test_validation_base_url_env_is_parsed():
|
||||
config = AuditaConfig.from_sources(env={"AUDITA_VALIDATION_BASE_URL": "http://localhost:9000/v1"})
|
||||
|
||||
assert config.validation_base_url == "http://localhost:9000/v1"
|
||||
|
||||
|
||||
def test_validation_max_retries_env_is_parsed():
|
||||
config = AuditaConfig.from_sources(env={"AUDITA_VALIDATION_MAX_RETRIES": "7"})
|
||||
|
||||
assert config.validation_max_retries == 7
|
||||
|
||||
|
||||
def test_validation_max_prompt_tokens_cli_override_takes_precedence():
|
||||
config = AuditaConfig.from_sources(
|
||||
env={"AUDITA_VALIDATION_MAX_PROMPT_TOKENS": "1024"},
|
||||
overrides=ConfigOverrides(validation_max_prompt_tokens=4096),
|
||||
)
|
||||
|
||||
assert config.validation_max_prompt_tokens == 4096
|
||||
|
||||
|
||||
def test_validation_max_prompt_tokens_env_is_parsed():
|
||||
config = AuditaConfig.from_sources(env={"AUDITA_VALIDATION_MAX_PROMPT_TOKENS": "1024"})
|
||||
|
||||
assert config.validation_max_prompt_tokens == 1024
|
||||
|
||||
|
||||
def test_target_sections_cli_override_takes_precedence():
|
||||
config = AuditaConfig.from_sources(
|
||||
env={"AUDITA_TARGET_SECTIONS": "2"},
|
||||
overrides=ConfigOverrides(target_sections=5),
|
||||
)
|
||||
|
||||
assert config.target_sections == 5
|
||||
|
||||
|
||||
def test_target_sections_env_is_parsed():
|
||||
config = AuditaConfig.from_sources(env={"AUDITA_TARGET_SECTIONS": "3"})
|
||||
|
||||
assert config.target_sections == 3
|
||||
|
||||
|
||||
def test_min_section_tokens_env_is_parsed():
|
||||
config = AuditaConfig.from_sources(env={"AUDITA_MIN_SECTION_TOKENS": "3000"})
|
||||
|
||||
assert config.min_section_tokens == 3000
|
||||
|
||||
|
||||
def test_generic_llm_api_key_env_is_read():
|
||||
config = AuditaConfig.from_sources(env={"AUDITA_LLM_API_KEY": "generic-key"})
|
||||
|
||||
assert config.api_key == "generic-key"
|
||||
|
||||
|
||||
def test_generic_llm_api_key_takes_precedence_over_openrouter_env():
|
||||
config = AuditaConfig.from_sources(
|
||||
env={
|
||||
"AUDITA_LLM_API_KEY": "generic-key",
|
||||
"OPENROUTER_API_KEY": "legacy-key",
|
||||
}
|
||||
)
|
||||
|
||||
assert config.api_key == "generic-key"
|
||||
|
||||
|
||||
def test_llm_api_key_cli_override_takes_precedence_over_env():
|
||||
config = AuditaConfig.from_sources(
|
||||
env={
|
||||
"AUDITA_LLM_API_KEY": "generic-key",
|
||||
"OPENROUTER_API_KEY": "legacy-key",
|
||||
},
|
||||
overrides=ConfigOverrides(llm_api_key="cli-key"),
|
||||
)
|
||||
|
||||
assert config.api_key == "cli-key"
|
||||
|
||||
|
||||
def test_blank_llm_api_key_override_resolves_to_none():
|
||||
config = AuditaConfig.from_sources(
|
||||
env={"OPENROUTER_API_KEY": "legacy-key"},
|
||||
overrides=ConfigOverrides(llm_api_key=" "),
|
||||
)
|
||||
|
||||
assert config.api_key is None
|
||||
|
||||
|
||||
def test_validation_llm_api_key_env_is_read():
|
||||
config = AuditaConfig.from_sources(env={"AUDITA_VALIDATION_LLM_API_KEY": "validation-key"})
|
||||
|
||||
assert config.validation_llm_api_key == "validation-key"
|
||||
|
||||
|
||||
def test_blank_validation_llm_api_key_override_disables_primary_fallback():
|
||||
config = AuditaConfig.from_sources(
|
||||
env={"AUDITA_LLM_API_KEY": "primary-key"},
|
||||
overrides=ConfigOverrides(validation_llm_api_key=" "),
|
||||
)
|
||||
|
||||
assert config.validation_llm_api_key == ""
|
||||
assert config.validation_llm_config().api_key == ""
|
||||
|
||||
|
||||
def test_effective_validation_fields_fall_back_to_primary_settings():
|
||||
config = AuditaConfig.from_sources(
|
||||
env={
|
||||
"AUDITA_LLM_API_KEY": "primary-key",
|
||||
"AUDITA_MODEL": "primary-model",
|
||||
"AUDITA_BASE_URL": "http://localhost:8000/v1",
|
||||
"AUDITA_LLM_TIMEOUT_SECONDS": "120",
|
||||
"AUDITA_LLM_CONCURRENCY": "5",
|
||||
"AUDITA_MAX_RETRIES": "9",
|
||||
}
|
||||
)
|
||||
|
||||
validation = config.validation_llm_config()
|
||||
|
||||
assert validation.api_key == "primary-key"
|
||||
assert validation.model == "primary-model"
|
||||
assert validation.base_url == "http://localhost:8000/v1"
|
||||
assert validation.llm_timeout_seconds == 120.0
|
||||
assert validation.llm_concurrency == 5
|
||||
assert validation.max_retries == 9
|
||||
|
||||
|
||||
def test_effective_validation_fields_use_overrides_when_set():
|
||||
config = AuditaConfig.from_sources(
|
||||
env={
|
||||
"AUDITA_LLM_API_KEY": "primary-key",
|
||||
"AUDITA_MODEL": "primary-model",
|
||||
"AUDITA_BASE_URL": "http://localhost:8000/v1",
|
||||
"AUDITA_LLM_TIMEOUT_SECONDS": "120",
|
||||
"AUDITA_LLM_CONCURRENCY": "5",
|
||||
"AUDITA_MAX_RETRIES": "9",
|
||||
"AUDITA_VALIDATION_LLM_API_KEY": "validation-key",
|
||||
"AUDITA_VALIDATION_MODEL": "validation-model",
|
||||
"AUDITA_VALIDATION_BASE_URL": "http://localhost:9000/v1",
|
||||
"AUDITA_VALIDATION_LLM_TIMEOUT_SECONDS": "240",
|
||||
"AUDITA_VALIDATION_LLM_CONCURRENCY": "3",
|
||||
"AUDITA_VALIDATION_MAX_RETRIES": "2",
|
||||
}
|
||||
)
|
||||
|
||||
validation = config.validation_llm_config()
|
||||
|
||||
assert validation.api_key == "validation-key"
|
||||
assert validation.model == "validation-model"
|
||||
assert validation.base_url == "http://localhost:9000/v1"
|
||||
assert validation.llm_timeout_seconds == 240.0
|
||||
assert validation.llm_concurrency == 3
|
||||
assert validation.max_retries == 2
|
||||
|
||||
|
||||
def test_module_key_overrides_take_precedence():
|
||||
config = AuditaConfig.from_sources(
|
||||
env={"AUDITA_MODULES": "grammar"},
|
||||
overrides=ConfigOverrides(module_keys="homophones,grammar"),
|
||||
)
|
||||
|
||||
assert config.module_keys == ("homophones", "grammar")
|
||||
|
||||
|
||||
def test_module_key_env_is_parsed_and_trimmed():
|
||||
config = AuditaConfig.from_sources(env={"AUDITA_MODULES": " glossary , grammar "})
|
||||
|
||||
assert config.module_keys == ("glossary", "grammar")
|
||||
|
||||
|
||||
def test_invalid_work_dir_retention_is_rejected():
|
||||
with pytest.raises(AuditaConfigError):
|
||||
AuditaConfig.from_sources(env={"AUDITA_WORK_DIR_RETENTION": "sometimes"})
|
||||
|
||||
|
||||
def test_report_dict_includes_llm_timeout_seconds():
|
||||
config = AuditaConfig.from_sources(env={"AUDITA_LLM_TIMEOUT_SECONDS": "321"})
|
||||
|
||||
assert config.to_report_dict()["llm_timeout_seconds"] == 321.0
|
||||
|
||||
|
||||
def test_report_dict_includes_target_sections():
|
||||
config = AuditaConfig.from_sources(env={"AUDITA_TARGET_SECTIONS": "7"})
|
||||
|
||||
assert config.to_report_dict()["target_sections"] == 7
|
||||
|
||||
|
||||
def test_report_dict_includes_effective_validation_llm_config():
|
||||
config = AuditaConfig.from_sources(
|
||||
env={
|
||||
"AUDITA_LLM_API_KEY": "primary-key",
|
||||
"AUDITA_VALIDATION_MODEL": "validation-model",
|
||||
}
|
||||
)
|
||||
|
||||
report = config.to_report_dict()
|
||||
|
||||
assert report["validation_model"] == "validation-model"
|
||||
assert report["validation_max_prompt_tokens"] == DEFAULT_VALIDATION_MAX_PROMPT_TOKENS
|
||||
assert report["effective_validation_llm"]["api_key_configured"] is True
|
||||
assert report["effective_validation_llm"]["model"] == "validation-model"
|
||||
|
||||
|
||||
def test_threshold_overrides_take_precedence():
|
||||
config = AuditaConfig.from_sources(
|
||||
env={
|
||||
"AUDITA_GLOSSARY_CONFIDENCE_THRESHOLD": "0.6",
|
||||
"AUDITA_GRAMMAR_CONFIDENCE_THRESHOLD": "0.65",
|
||||
"AUDITA_HOMOPHONES_CONFIDENCE_THRESHOLD": "0.7",
|
||||
"AUDITA_SPOKEN_WORD_CONFIDENCE_THRESHOLD": "0.75",
|
||||
},
|
||||
overrides=ConfigOverrides(
|
||||
glossary_confidence_threshold=0.85,
|
||||
grammar_confidence_threshold=0.88,
|
||||
homophones_confidence_threshold=0.9,
|
||||
spoken_word_confidence_threshold=0.95,
|
||||
),
|
||||
)
|
||||
|
||||
assert config.glossary_confidence_threshold == 0.85
|
||||
assert config.grammar_confidence_threshold == 0.88
|
||||
assert config.homophones_confidence_threshold == 0.9
|
||||
assert config.spoken_word_confidence_threshold == 0.95
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value",
|
||||
[
|
||||
"",
|
||||
"grammar,,homophones",
|
||||
"bogus",
|
||||
],
|
||||
)
|
||||
def test_invalid_module_sequences_are_rejected(value):
|
||||
with pytest.raises(AuditaConfigError, match="AUDITA_MODULES"):
|
||||
AuditaConfig.from_sources(env={"AUDITA_MODULES": value})
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"env_name",
|
||||
[
|
||||
"AUDITA_GLOSSARY_CONFIDENCE_THRESHOLD",
|
||||
"AUDITA_GRAMMAR_CONFIDENCE_THRESHOLD",
|
||||
"AUDITA_HOMOPHONES_CONFIDENCE_THRESHOLD",
|
||||
"AUDITA_SPOKEN_WORD_CONFIDENCE_THRESHOLD",
|
||||
],
|
||||
)
|
||||
def test_invalid_thresholds_are_rejected(env_name):
|
||||
with pytest.raises(AuditaConfigError):
|
||||
AuditaConfig.from_sources(env={env_name: "1.5"})
|
||||
|
||||
|
||||
@pytest.mark.parametrize("value", ["0", "-1", "many"])
|
||||
def test_invalid_llm_concurrency_is_rejected(value):
|
||||
with pytest.raises(AuditaConfigError, match="AUDITA_LLM_CONCURRENCY"):
|
||||
AuditaConfig.from_sources(env={"AUDITA_LLM_CONCURRENCY": value})
|
||||
|
||||
|
||||
@pytest.mark.parametrize("value", ["0", "-1", "many"])
|
||||
def test_invalid_llm_timeout_seconds_is_rejected(value):
|
||||
with pytest.raises(AuditaConfigError, match="AUDITA_LLM_TIMEOUT_SECONDS"):
|
||||
AuditaConfig.from_sources(env={"AUDITA_LLM_TIMEOUT_SECONDS": value})
|
||||
|
||||
|
||||
@pytest.mark.parametrize("value", ["0", "-1", "many"])
|
||||
def test_invalid_validation_llm_concurrency_is_rejected(value):
|
||||
with pytest.raises(AuditaConfigError, match="AUDITA_VALIDATION_LLM_CONCURRENCY"):
|
||||
AuditaConfig.from_sources(env={"AUDITA_VALIDATION_LLM_CONCURRENCY": value})
|
||||
|
||||
|
||||
@pytest.mark.parametrize("value", ["0", "-1", "many"])
|
||||
def test_invalid_validation_llm_timeout_seconds_is_rejected(value):
|
||||
with pytest.raises(AuditaConfigError, match="AUDITA_VALIDATION_LLM_TIMEOUT_SECONDS"):
|
||||
AuditaConfig.from_sources(env={"AUDITA_VALIDATION_LLM_TIMEOUT_SECONDS": value})
|
||||
|
||||
|
||||
@pytest.mark.parametrize("value", ["-1", "many"])
|
||||
def test_invalid_validation_max_retries_is_rejected(value):
|
||||
with pytest.raises(AuditaConfigError, match="AUDITA_VALIDATION_MAX_RETRIES"):
|
||||
AuditaConfig.from_sources(env={"AUDITA_VALIDATION_MAX_RETRIES": value})
|
||||
|
||||
|
||||
@pytest.mark.parametrize("value", ["0", "-1", "many"])
|
||||
def test_invalid_validation_max_prompt_tokens_is_rejected(value):
|
||||
with pytest.raises(AuditaConfigError, match="AUDITA_VALIDATION_MAX_PROMPT_TOKENS"):
|
||||
AuditaConfig.from_sources(env={"AUDITA_VALIDATION_MAX_PROMPT_TOKENS": value})
|
||||
|
||||
|
||||
@pytest.mark.parametrize("value", ["0", "-1", "many"])
|
||||
def test_invalid_target_sections_is_rejected(value):
|
||||
with pytest.raises(AuditaConfigError, match="AUDITA_TARGET_SECTIONS"):
|
||||
AuditaConfig.from_sources(env={"AUDITA_TARGET_SECTIONS": value})
|
||||
|
||||
|
||||
@pytest.mark.parametrize("value", ["0", "-1", "many"])
|
||||
def test_invalid_min_section_tokens_is_rejected(value):
|
||||
with pytest.raises(AuditaConfigError, match="AUDITA_MIN_SECTION_TOKENS"):
|
||||
AuditaConfig.from_sources(env={"AUDITA_MIN_SECTION_TOKENS": value})
|
||||
|
||||
|
||||
def test_min_section_tokens_must_not_exceed_max_section_tokens():
|
||||
with pytest.raises(AuditaConfigError, match="AUDITA_MIN_SECTION_TOKENS"):
|
||||
AuditaConfig.from_sources(
|
||||
env={
|
||||
"AUDITA_MIN_SECTION_TOKENS": "9000",
|
||||
"AUDITA_MAX_SECTION_TOKENS": "8000",
|
||||
}
|
||||
)
|
||||
Reference in New Issue
Block a user