mirror of
https://github.com/katanemo/plano.git
synced 2026-04-25 00:36:34 +02:00
272 lines
7.2 KiB
Python
272 lines
7.2 KiB
Python
import pytest
|
|
from unittest import mock
|
|
import sys
|
|
from cli.config_generator import validate_and_render_schema
|
|
|
|
# Patch sys.path to allow import from cli/
|
|
import os
|
|
|
|
sys.path.insert(
|
|
0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "cli"))
|
|
)
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def cleanup_env(monkeypatch):
|
|
# Clean up environment variables and mocks after each test
|
|
yield
|
|
monkeypatch.undo()
|
|
|
|
|
|
def test_validate_and_render_happy_path(monkeypatch):
|
|
monkeypatch.setenv("ARCH_CONFIG_FILE", "fake_arch_config.yaml")
|
|
monkeypatch.setenv("ARCH_CONFIG_SCHEMA_FILE", "fake_arch_config_schema.yaml")
|
|
monkeypatch.setenv("ENVOY_CONFIG_TEMPLATE_FILE", "./envoy.template.yaml")
|
|
monkeypatch.setenv("ARCH_CONFIG_FILE_RENDERED", "fake_arch_config_rendered.yaml")
|
|
monkeypatch.setenv("ENVOY_CONFIG_FILE_RENDERED", "fake_envoy.yaml")
|
|
monkeypatch.setenv("TEMPLATE_ROOT", "../")
|
|
|
|
arch_config = """
|
|
version: v0.1.0
|
|
|
|
listeners:
|
|
egress_traffic:
|
|
address: 0.0.0.0
|
|
port: 12000
|
|
message_format: openai
|
|
timeout: 30s
|
|
|
|
llm_providers:
|
|
|
|
- model: openai/gpt-4o-mini
|
|
access_key: $OPENAI_API_KEY
|
|
default: true
|
|
|
|
- model: openai/gpt-4o
|
|
access_key: $OPENAI_API_KEY
|
|
routing_preferences:
|
|
- name: code understanding
|
|
description: understand and explain existing code snippets, functions, or libraries
|
|
|
|
- model: openai/gpt-4.1
|
|
access_key: $OPENAI_API_KEY
|
|
routing_preferences:
|
|
- name: code generation
|
|
description: generating new code snippets, functions, or boilerplate based on user prompts or requirements
|
|
|
|
tracing:
|
|
random_sampling: 100
|
|
"""
|
|
arch_config_schema = ""
|
|
with open("../arch_config_schema.yaml", "r") as file:
|
|
arch_config_schema = file.read()
|
|
|
|
m_open = mock.mock_open()
|
|
# Provide enough file handles for all open() calls in validate_and_render_schema
|
|
m_open.side_effect = [
|
|
mock.mock_open(read_data="").return_value,
|
|
mock.mock_open(read_data=arch_config).return_value, # ARCH_CONFIG_FILE
|
|
mock.mock_open(
|
|
read_data=arch_config_schema
|
|
).return_value, # ARCH_CONFIG_SCHEMA_FILE
|
|
mock.mock_open(read_data=arch_config).return_value, # ARCH_CONFIG_FILE
|
|
mock.mock_open(
|
|
read_data=arch_config_schema
|
|
).return_value, # ARCH_CONFIG_SCHEMA_FILE
|
|
mock.mock_open().return_value, # ENVOY_CONFIG_FILE_RENDERED (write)
|
|
mock.mock_open().return_value, # ARCH_CONFIG_FILE_RENDERED (write)
|
|
]
|
|
with mock.patch("builtins.open", m_open):
|
|
with mock.patch("config_generator.Environment"):
|
|
validate_and_render_schema()
|
|
|
|
|
|
arch_config_test_cases = [
|
|
{
|
|
"id": "duplicate_provider_name",
|
|
"expected_error": "Duplicate llm_provider name",
|
|
"arch_config": """
|
|
version: v0.1.0
|
|
|
|
listeners:
|
|
egress_traffic:
|
|
address: 0.0.0.0
|
|
port: 12000
|
|
message_format: openai
|
|
timeout: 30s
|
|
|
|
llm_providers:
|
|
|
|
- name: test1
|
|
model: openai/gpt-4o
|
|
access_key: $OPENAI_API_KEY
|
|
|
|
- name: test1
|
|
model: openai/gpt-4o
|
|
access_key: $OPENAI_API_KEY
|
|
|
|
""",
|
|
},
|
|
{
|
|
"id": "provider_interface_with_model_id",
|
|
"expected_error": "Please provide provider interface as part of model name",
|
|
"arch_config": """
|
|
version: v0.1.0
|
|
|
|
listeners:
|
|
egress_traffic:
|
|
address: 0.0.0.0
|
|
port: 12000
|
|
message_format: openai
|
|
timeout: 30s
|
|
|
|
llm_providers:
|
|
|
|
- model: openai/gpt-4o
|
|
access_key: $OPENAI_API_KEY
|
|
provider_interface: openai
|
|
|
|
""",
|
|
},
|
|
{
|
|
"id": "duplicate_model_id",
|
|
"expected_error": "Duplicate model_id",
|
|
"arch_config": """
|
|
version: v0.1.0
|
|
|
|
listeners:
|
|
egress_traffic:
|
|
address: 0.0.0.0
|
|
port: 12000
|
|
message_format: openai
|
|
timeout: 30s
|
|
|
|
llm_providers:
|
|
|
|
- model: openai/gpt-4o
|
|
access_key: $OPENAI_API_KEY
|
|
|
|
- model: mistral/gpt-4o
|
|
|
|
""",
|
|
},
|
|
{
|
|
"id": "custom_provider_base_url",
|
|
"expected_error": "Must provide base_url and provider_interface",
|
|
"arch_config": """
|
|
version: v0.1.0
|
|
|
|
listeners:
|
|
egress_traffic:
|
|
address: 0.0.0.0
|
|
port: 12000
|
|
message_format: openai
|
|
timeout: 30s
|
|
|
|
llm_providers:
|
|
|
|
- model: custom/gpt-4o
|
|
|
|
""",
|
|
},
|
|
{
|
|
"id": "base_url_no_prefix",
|
|
"expected_error": "Please provide base_url without path",
|
|
"arch_config": """
|
|
version: v0.1.0
|
|
|
|
listeners:
|
|
egress_traffic:
|
|
address: 0.0.0.0
|
|
port: 12000
|
|
message_format: openai
|
|
timeout: 30s
|
|
|
|
llm_providers:
|
|
|
|
- model: custom/gpt-4o
|
|
base_url: "http://custom.com/test"
|
|
provider_interface: openai
|
|
|
|
""",
|
|
},
|
|
{
|
|
"id": "duplicate_routeing_preference_name",
|
|
"expected_error": "Duplicate routing preference name",
|
|
"arch_config": """
|
|
version: v0.1.0
|
|
|
|
listeners:
|
|
egress_traffic:
|
|
address: 0.0.0.0
|
|
port: 12000
|
|
message_format: openai
|
|
timeout: 30s
|
|
|
|
llm_providers:
|
|
|
|
- model: openai/gpt-4o-mini
|
|
access_key: $OPENAI_API_KEY
|
|
default: true
|
|
|
|
- model: openai/gpt-4o
|
|
access_key: $OPENAI_API_KEY
|
|
routing_preferences:
|
|
- name: code understanding
|
|
description: understand and explain existing code snippets, functions, or libraries
|
|
|
|
- model: openai/gpt-4.1
|
|
access_key: $OPENAI_API_KEY
|
|
routing_preferences:
|
|
- name: code understanding
|
|
description: generating new code snippets, functions, or boilerplate based on user prompts or requirements
|
|
|
|
tracing:
|
|
random_sampling: 100
|
|
|
|
""",
|
|
},
|
|
]
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"arch_config_test_case",
|
|
arch_config_test_cases,
|
|
ids=[case["id"] for case in arch_config_test_cases],
|
|
)
|
|
def test_validate_and_render_schema_tests(monkeypatch, arch_config_test_case):
|
|
monkeypatch.setenv("ARCH_CONFIG_FILE", "fake_arch_config.yaml")
|
|
monkeypatch.setenv("ARCH_CONFIG_SCHEMA_FILE", "fake_arch_config_schema.yaml")
|
|
monkeypatch.setenv("ENVOY_CONFIG_TEMPLATE_FILE", "./envoy.template.yaml")
|
|
monkeypatch.setenv("ARCH_CONFIG_FILE_RENDERED", "fake_arch_config_rendered.yaml")
|
|
monkeypatch.setenv("ENVOY_CONFIG_FILE_RENDERED", "fake_envoy.yaml")
|
|
monkeypatch.setenv("TEMPLATE_ROOT", "../")
|
|
|
|
arch_config = arch_config_test_case["arch_config"]
|
|
expected_error = arch_config_test_case["expected_error"]
|
|
test_id = arch_config_test_case["id"]
|
|
|
|
arch_config_schema = ""
|
|
with open("../arch_config_schema.yaml", "r") as file:
|
|
arch_config_schema = file.read()
|
|
|
|
m_open = mock.mock_open()
|
|
# Provide enough file handles for all open() calls in validate_and_render_schema
|
|
m_open.side_effect = [
|
|
mock.mock_open(read_data="").return_value,
|
|
mock.mock_open(read_data=arch_config).return_value, # ARCH_CONFIG_FILE
|
|
mock.mock_open(
|
|
read_data=arch_config_schema
|
|
).return_value, # ARCH_CONFIG_SCHEMA_FILE
|
|
mock.mock_open(read_data=arch_config).return_value, # ARCH_CONFIG_FILE
|
|
mock.mock_open(
|
|
read_data=arch_config_schema
|
|
).return_value, # ARCH_CONFIG_SCHEMA_FILE
|
|
mock.mock_open().return_value, # ENVOY_CONFIG_FILE_RENDERED (write)
|
|
mock.mock_open().return_value, # ARCH_CONFIG_FILE_RENDERED (write)
|
|
]
|
|
with mock.patch("builtins.open", m_open):
|
|
with mock.patch("config_generator.Environment"):
|
|
with pytest.raises(Exception) as excinfo:
|
|
validate_and_render_schema()
|
|
assert expected_error in str(excinfo.value)
|