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_NORMALIZE_MAX_SEGMENT_GAP, DEFAULT_SPOKEN_WORD_CONFIDENCE_THRESHOLD, 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.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), ) assert config.max_section_tokens == 2000 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_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_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_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})