#!/usr/bin/env python3 """Tests for deploy_config_validator.py""" import json import sys import os import pytest sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) from scripts.deploy_config_validator import ( validate_yaml_syntax, validate_required_keys, validate_provider_chain, validate_value_types, validate_config, detect_config_type, ValidationError, ) class TestYAMLSyntax: def test_valid_yaml(self): data, errors = validate_yaml_syntax("key: value\nlist:\n - a\n - b\n") assert data is not None assert len(errors) == 0 def test_invalid_yaml(self): data, errors = validate_yaml_syntax("key: [unclosed") assert data is None assert len(errors) > 0 def test_empty_yaml(self): data, errors = validate_yaml_syntax("") assert data is None assert any("empty" in e.message for e in errors) def test_tabs_warning(self): data, errors = validate_yaml_syntax("key:\tvalue\n") assert any("tab" in e.message for e in errors) class TestRequiredKeys: def test_missing_key(self): errors = validate_required_keys({}, "hermes") assert any("providers" in e.message for e in errors) def test_wrong_type(self): errors = validate_required_keys({"providers": "not-a-list"}, "hermes") assert any("expected list" in e.message for e in errors) def test_valid(self): errors = validate_required_keys({"providers": []}, "hermes") provider_errors = [e for e in errors if "providers" in e.message and "missing" in e.message] assert len(provider_errors) == 0 class TestProviderChain: def test_empty_providers(self): errors = validate_provider_chain({"providers": []}) assert any("empty" in e.message for e in errors) def test_missing_name(self): errors = validate_provider_chain({"providers": [{"model": "test", "base_url": "http://x"}]}) assert any("name" in e.message and "missing" in e.message for e in errors) def test_banned_provider(self): errors = validate_provider_chain({"providers": [ {"name": "anthropic", "model": "claude-3", "base_url": "http://x"} ]}) assert any("banned provider" in e.message for e in errors) def test_banned_model(self): errors = validate_provider_chain({"providers": [ {"name": "test", "model": "claude-sonnet-4", "base_url": "http://x"} ]}) assert any("banned model" in e.message for e in errors) def test_valid_providers(self): errors = validate_provider_chain({"providers": [ {"name": "kimi-coding", "model": "kimi-k2.5", "base_url": "https://api.kimi.com/v1"} ]}) provider_errors = [e for e in errors if e.severity == "error"] assert len(provider_errors) == 0 class TestValueTypes: def test_string_port(self): errors = validate_value_types({"port": "8080"}) assert any("port" in e.path and "number" in e.message for e in errors) def test_valid_port(self): errors = validate_value_types({"port": 8080}) port_errors = [e for e in errors if "port" in e.path] assert len(port_errors) == 0 def test_bad_url(self): errors = validate_value_types({"base_url": "not-a-url"}) assert any("URL" in e.message for e in errors) class TestDetectConfigType: def test_hermes(self): t = detect_config_type({"providers": [], "display": {}}) assert t == "hermes" def test_ansible(self): t = detect_config_type({"all": {"children": {"wizards": {}}}}) assert t == "ansible_inventory" def test_unknown(self): t = detect_config_type({"random": "data"}) assert t == "any" class TestFullValidation: def test_valid_hermes_config(self): text = """ providers: - name: kimi-coding model: kimi-k2.5 base_url: https://api.kimi.com/coding/v1 timeout: 120 display: skin: default """ errors = validate_config(text, "hermes") assert not any(e.severity == "error" for e in errors) def test_banned_provider_catches(self): text = """ providers: - name: anthropic model: claude-sonnet-4 base_url: https://api.anthropic.com """ errors = validate_config(text, "hermes") assert any("banned" in e.message for e in errors) def test_missing_providers(self): text = "display:\n skin: default\n" errors = validate_config(text, "hermes") assert any("providers" in e.message and "missing" in e.message for e in errors)