feat: establish voice assistant V1 baseline
This commit is contained in:
288
tests/test_config.py
Normal file
288
tests/test_config.py
Normal file
@@ -0,0 +1,288 @@
|
||||
"""
|
||||
Typed configuration models for the Voice Assistant.
|
||||
|
||||
Purpose:
|
||||
Define and validate the configuration structures used by the
|
||||
application and dynamic provider loading system.
|
||||
|
||||
Responsibilities:
|
||||
- Define the application configuration for STT, Agent, and TTS.
|
||||
- Define the provider catalog structure.
|
||||
- Represent provider-specific options without coupling the core
|
||||
configuration model to a specific vendor or implementation.
|
||||
- Describe provider capabilities for discovery and future tooling.
|
||||
|
||||
Non-responsibilities:
|
||||
- Loading YAML files.
|
||||
- Reading environment variables or secrets.
|
||||
- Selecting or instantiating provider implementations.
|
||||
- Executing STT, Agent, or TTS functionality.
|
||||
|
||||
Architecture:
|
||||
YAML
|
||||
|
|
||||
v
|
||||
Config Loader
|
||||
|
|
||||
v
|
||||
AppConfig / ProviderCatalog
|
||||
|
|
||||
v
|
||||
Provider Loader
|
||||
|
|
||||
v
|
||||
STTAdapter / AgentAdapter / TTSAdapter
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import yaml
|
||||
|
||||
from voice_assistant.config.loader import load_config
|
||||
from voice_assistant.config.loader import (
|
||||
load_config,
|
||||
load_provider_catalog,
|
||||
)
|
||||
|
||||
|
||||
def test_load_development_config():
|
||||
config = load_config()
|
||||
|
||||
assert config.stt.provider == "nvidia"
|
||||
assert config.stt.options["server"] == "127.0.0.1:9000"
|
||||
assert config.stt.options["language_code"] == "pt-BR"
|
||||
assert config.stt.options["automatic_punctuation"] is True
|
||||
|
||||
assert config.tts.provider == "voxcpm"
|
||||
assert config.tts.options["server"] == "http://127.0.0.1:8000"
|
||||
assert config.tts.options["model"] == "openbmb/VoxCPM2"
|
||||
assert config.tts.options["voice"] == "default"
|
||||
|
||||
assert config.agent.provider == "mock"
|
||||
|
||||
|
||||
def test_load_config_missing_file(tmp_path):
|
||||
with pytest.raises(
|
||||
FileNotFoundError,
|
||||
match="Configuration file not found",
|
||||
):
|
||||
load_config(
|
||||
environment="development",
|
||||
config_dir=tmp_path,
|
||||
)
|
||||
|
||||
|
||||
def test_load_config_empty_file(tmp_path):
|
||||
config_file = tmp_path / "development.yaml"
|
||||
config_file.write_text(
|
||||
"",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
with pytest.raises(
|
||||
ValueError,
|
||||
match="Configuration file is empty",
|
||||
):
|
||||
load_config(
|
||||
environment="development",
|
||||
config_dir=tmp_path,
|
||||
)
|
||||
|
||||
|
||||
def test_load_config_root_must_be_mapping(tmp_path):
|
||||
config_file = tmp_path / "development.yaml"
|
||||
config_file.write_text(
|
||||
"- item1\n- item2\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
with pytest.raises(
|
||||
ValueError,
|
||||
match="Configuration root must be a mapping",
|
||||
):
|
||||
load_config(
|
||||
environment="development",
|
||||
config_dir=tmp_path,
|
||||
)
|
||||
|
||||
|
||||
def test_load_config_validates_required_fields(tmp_path):
|
||||
config_file = tmp_path / "development.yaml"
|
||||
|
||||
config_file.write_text(
|
||||
yaml.safe_dump(
|
||||
{
|
||||
"stt": {
|
||||
"provider": "nvidia",
|
||||
"server": "127.0.0.1:9000",
|
||||
"language": "pt-BR",
|
||||
},
|
||||
"tts": {
|
||||
"provider": "voxcpm",
|
||||
"server": "http://127.0.0.1:8000",
|
||||
"model": "openbmb/VoxCPM2",
|
||||
"voice": "default",
|
||||
},
|
||||
# agent intentionally missing
|
||||
}
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
with pytest.raises(
|
||||
Exception,
|
||||
match="agent",
|
||||
):
|
||||
load_config(
|
||||
environment="development",
|
||||
config_dir=tmp_path,
|
||||
)
|
||||
|
||||
def test_load_provider_catalog():
|
||||
catalog = load_provider_catalog()
|
||||
|
||||
assert "nvidia" in catalog.providers["stt"]
|
||||
assert "voxcpm" in catalog.providers["tts"]
|
||||
assert "mock" in catalog.providers["agent"]
|
||||
|
||||
voxcpm = catalog.providers["tts"]["voxcpm"]
|
||||
|
||||
assert voxcpm.name == "VoxCPM"
|
||||
assert (
|
||||
voxcpm.implementation
|
||||
== "voice_assistant.tts.voxcpm:VoxCPMTTSAdapter"
|
||||
)
|
||||
assert voxcpm.capabilities == {
|
||||
"realtime": True,
|
||||
"streaming": True,
|
||||
}
|
||||
|
||||
|
||||
def test_load_provider_catalog_missing_file(tmp_path):
|
||||
with pytest.raises(
|
||||
FileNotFoundError,
|
||||
match="Provider catalog not found",
|
||||
):
|
||||
load_provider_catalog(
|
||||
config_dir=tmp_path,
|
||||
)
|
||||
|
||||
|
||||
def test_load_config_uses_app_env(
|
||||
tmp_path,
|
||||
monkeypatch,
|
||||
):
|
||||
development = tmp_path / "development.yaml"
|
||||
|
||||
development.write_text(
|
||||
"""
|
||||
stt:
|
||||
provider: nvidia
|
||||
options:
|
||||
server: "dev-asr"
|
||||
|
||||
tts:
|
||||
provider: voxcpm
|
||||
options:
|
||||
server: "dev-tts"
|
||||
model: "dev-model"
|
||||
voice: "default"
|
||||
|
||||
agent:
|
||||
provider: mock
|
||||
options: {}
|
||||
""",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
monkeypatch.setenv(
|
||||
"APP_ENV",
|
||||
"development",
|
||||
)
|
||||
|
||||
config = load_config(
|
||||
config_dir=tmp_path,
|
||||
)
|
||||
|
||||
assert config.stt.options["server"] == "dev-asr"
|
||||
assert config.tts.options["server"] == "dev-tts"
|
||||
assert config.tts.options["model"] == "dev-model"
|
||||
|
||||
|
||||
def test_load_config_rejects_unknown_environment(
|
||||
tmp_path,
|
||||
):
|
||||
with pytest.raises(
|
||||
FileNotFoundError,
|
||||
match="Configuration file not found",
|
||||
):
|
||||
load_config(
|
||||
environment="does-not-exist",
|
||||
config_dir=tmp_path,
|
||||
)
|
||||
|
||||
def test_load_config_uses_app_env(
|
||||
tmp_path,
|
||||
monkeypatch,
|
||||
):
|
||||
config_file = tmp_path / "staging.yaml"
|
||||
|
||||
config_file.write_text(
|
||||
"""
|
||||
stt:
|
||||
provider: nvidia
|
||||
options:
|
||||
server: "staging-asr:9000"
|
||||
|
||||
tts:
|
||||
provider: voxcpm
|
||||
options:
|
||||
server: "http://staging-tts:8000"
|
||||
model: "staging-model"
|
||||
voice: "staging-voice"
|
||||
|
||||
agent:
|
||||
provider: mock
|
||||
options: {}
|
||||
""",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
monkeypatch.setenv(
|
||||
"APP_ENV",
|
||||
"staging",
|
||||
)
|
||||
|
||||
config = load_config(
|
||||
config_dir=tmp_path,
|
||||
)
|
||||
|
||||
assert config.stt.provider == "nvidia"
|
||||
assert (
|
||||
config.stt.options["server"]
|
||||
== "staging-asr:9000"
|
||||
)
|
||||
|
||||
assert config.tts.provider == "voxcpm"
|
||||
assert (
|
||||
config.tts.options["server"]
|
||||
== "http://staging-tts:8000"
|
||||
)
|
||||
|
||||
assert config.tts.options["model"] == "staging-model"
|
||||
assert config.tts.options["voice"] == "staging-voice"
|
||||
|
||||
assert config.agent.provider == "mock"
|
||||
|
||||
|
||||
def test_load_config_rejects_unknown_environment(
|
||||
tmp_path,
|
||||
):
|
||||
with pytest.raises(
|
||||
FileNotFoundError,
|
||||
match="Configuration file not found",
|
||||
):
|
||||
load_config(
|
||||
environment="does-not-exist",
|
||||
config_dir=tmp_path,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user