openwebui-cli/tests/test_chat_request_options.py
2025-12-01 04:24:51 +01:00

375 lines
12 KiB
Python

"""Tests for chat request body population with options."""
import json
from unittest.mock import MagicMock, Mock, patch
import pytest
from typer.testing import CliRunner
from openwebui_cli.main import app
runner = CliRunner()
@pytest.fixture
def mock_config(tmp_path, monkeypatch):
"""Mock configuration for testing."""
config_dir = tmp_path / "openwebui"
config_path = config_dir / "config.yaml"
monkeypatch.setattr("openwebui_cli.config.get_config_dir", lambda: config_dir)
monkeypatch.setattr("openwebui_cli.config.get_config_path", lambda: config_path)
# Create default config
from openwebui_cli.config import Config, save_config
config = Config()
save_config(config)
return config_path
@pytest.fixture
def mock_keyring(monkeypatch):
"""Mock keyring for testing."""
token_store = {}
def get_password(service, key):
return token_store.get(f"{service}:{key}")
def set_password(service, key, password):
token_store[f"{service}:{key}"] = password
monkeypatch.setattr("keyring.get_password", get_password)
monkeypatch.setattr("keyring.set_password", set_password)
def _create_mock_client(response_data=None):
"""Helper to create a mock HTTP client."""
if response_data is None:
response_data = {
"choices": [
{
"message": {
"content": "Test response"
}
}
]
}
mock_http_client = MagicMock()
mock_http_client.__enter__.return_value = mock_http_client
mock_http_client.__exit__.return_value = None
mock_response = Mock()
mock_response.status_code = 200
mock_response.json.return_value = response_data
mock_http_client.post.return_value = mock_response
return mock_http_client
@patch("openwebui_cli.commands.chat.create_client")
def test_chat_id_in_body(mock_create_client, mock_config, mock_keyring):
"""Test --chat-id is included in request body."""
mock_http_client = _create_mock_client()
mock_create_client.return_value = mock_http_client
result = runner.invoke(
app,
[
"chat", "send",
"--model", "test-model",
"--no-stream",
"--chat-id", "my-chat-123",
"--prompt", "Hello"
],
)
assert result.exit_code == 0, f"Command failed with output: {result.stdout}"
# Verify the request was made with chat_id in body
call_args = mock_http_client.post.call_args
assert call_args is not None, "post() was not called"
body = call_args.kwargs["json"]
assert "chat_id" in body, f"chat_id not in request body. Body: {body}"
assert body["chat_id"] == "my-chat-123", f"Expected 'my-chat-123', got {body['chat_id']}"
@patch("openwebui_cli.commands.chat.create_client")
def test_temperature_in_body(mock_create_client, mock_config, mock_keyring):
"""Test --temperature is included in request body."""
mock_http_client = _create_mock_client()
mock_create_client.return_value = mock_http_client
result = runner.invoke(
app,
[
"chat", "send",
"--model", "test-model",
"--no-stream",
"--temperature", "0.7",
"--prompt", "Hello"
],
)
assert result.exit_code == 0, f"Command failed with output: {result.stdout}"
# Verify the request was made with temperature in body
call_args = mock_http_client.post.call_args
assert call_args is not None, "post() was not called"
body = call_args.kwargs["json"]
assert "temperature" in body, f"temperature not in request body. Body: {body}"
assert body["temperature"] == 0.7, f"Expected 0.7, got {body['temperature']}"
@patch("openwebui_cli.commands.chat.create_client")
def test_max_tokens_in_body(mock_create_client, mock_config, mock_keyring):
"""Test --max-tokens is included in request body."""
mock_http_client = _create_mock_client()
mock_create_client.return_value = mock_http_client
result = runner.invoke(
app,
[
"chat", "send",
"--model", "test-model",
"--no-stream",
"--max-tokens", "1000",
"--prompt", "Hello"
],
)
assert result.exit_code == 0, f"Command failed with output: {result.stdout}"
# Verify the request was made with max_tokens in body
call_args = mock_http_client.post.call_args
assert call_args is not None, "post() was not called"
body = call_args.kwargs["json"]
assert "max_tokens" in body, f"max_tokens not in request body. Body: {body}"
assert body["max_tokens"] == 1000, f"Expected 1000, got {body['max_tokens']}"
@patch("openwebui_cli.commands.chat.create_client")
def test_all_options_combined(mock_create_client, mock_config, mock_keyring):
"""Test all request body options together."""
mock_http_client = _create_mock_client()
mock_create_client.return_value = mock_http_client
result = runner.invoke(
app,
[
"chat", "send",
"--model", "test-model",
"--no-stream",
"--chat-id", "combined-chat-456",
"--temperature", "0.5",
"--max-tokens", "2000",
"--prompt", "Hello"
],
)
assert result.exit_code == 0, f"Command failed with output: {result.stdout}"
# Verify all options are in the request body
call_args = mock_http_client.post.call_args
assert call_args is not None, "post() was not called"
body = call_args.kwargs["json"]
# Verify chat_id
assert "chat_id" in body, f"chat_id not in request body. Body: {body}"
assert body["chat_id"] == "combined-chat-456"
# Verify temperature
assert "temperature" in body, f"temperature not in request body. Body: {body}"
assert body["temperature"] == 0.5
# Verify max_tokens
assert "max_tokens" in body, f"max_tokens not in request body. Body: {body}"
assert body["max_tokens"] == 2000
# Verify core fields are still present
assert "model" in body
assert body["model"] == "test-model"
assert "messages" in body
assert "stream" in body
@patch("openwebui_cli.commands.chat.create_client")
def test_temperature_with_different_values(mock_create_client, mock_config, mock_keyring):
"""Test temperature with various valid values."""
test_values = [0.0, 0.3, 1.0, 1.5, 2.0]
for temp_value in test_values:
mock_http_client = _create_mock_client()
mock_create_client.return_value = mock_http_client
result = runner.invoke(
app,
[
"chat", "send",
"--model", "test-model",
"--no-stream",
"--temperature", str(temp_value),
"--prompt", "Hello"
],
)
assert result.exit_code == 0, f"Command failed for temperature {temp_value}"
call_args = mock_http_client.post.call_args
body = call_args.kwargs["json"]
assert body["temperature"] == temp_value, f"Temperature mismatch for {temp_value}"
@patch("openwebui_cli.commands.chat.create_client")
def test_max_tokens_with_different_values(mock_create_client, mock_config, mock_keyring):
"""Test max-tokens with various values."""
test_values = [100, 500, 1000, 4000, 8000]
for token_value in test_values:
mock_http_client = _create_mock_client()
mock_create_client.return_value = mock_http_client
result = runner.invoke(
app,
[
"chat", "send",
"--model", "test-model",
"--no-stream",
"--max-tokens", str(token_value),
"--prompt", "Hello"
],
)
assert result.exit_code == 0, f"Command failed for max-tokens {token_value}"
call_args = mock_http_client.post.call_args
body = call_args.kwargs["json"]
assert body["max_tokens"] == token_value, f"Max tokens mismatch for {token_value}"
@patch("openwebui_cli.commands.chat.create_client")
def test_options_not_in_body_when_not_provided(mock_create_client, mock_config, mock_keyring):
"""Test that optional fields are not in body when not provided."""
mock_http_client = _create_mock_client()
mock_create_client.return_value = mock_http_client
result = runner.invoke(
app,
[
"chat", "send",
"--model", "test-model",
"--no-stream",
"--prompt", "Hello"
],
)
assert result.exit_code == 0
call_args = mock_http_client.post.call_args
body = call_args.kwargs["json"]
# These should not be in the body when not provided
assert "chat_id" not in body, "chat_id should not be in body when not provided"
assert "temperature" not in body, "temperature should not be in body when not provided"
assert "max_tokens" not in body, "max_tokens should not be in body when not provided"
@patch("openwebui_cli.commands.chat.create_client")
def test_chat_id_with_special_characters(mock_create_client, mock_config, mock_keyring):
"""Test chat-id with special characters and UUID-like format."""
special_ids = [
"uuid-12345-67890-abcdef",
"chat_2025_01_01_001",
"conversation-abc123xyz",
]
for chat_id_value in special_ids:
mock_http_client = _create_mock_client()
mock_create_client.return_value = mock_http_client
result = runner.invoke(
app,
[
"chat", "send",
"--model", "test-model",
"--no-stream",
"--chat-id", chat_id_value,
"--prompt", "Hello"
],
)
assert result.exit_code == 0, f"Command failed for chat_id {chat_id_value}"
call_args = mock_http_client.post.call_args
body = call_args.kwargs["json"]
assert body["chat_id"] == chat_id_value, f"Chat ID mismatch for {chat_id_value}"
@patch("openwebui_cli.commands.chat.create_client")
def test_request_body_has_core_fields(mock_create_client, mock_config, mock_keyring):
"""Test that core request fields are always present."""
mock_http_client = _create_mock_client()
mock_create_client.return_value = mock_http_client
result = runner.invoke(
app,
[
"chat", "send",
"--model", "test-model",
"--no-stream",
"--prompt", "Hello"
],
)
assert result.exit_code == 0
call_args = mock_http_client.post.call_args
body = call_args.kwargs["json"]
# Core fields that should always be present
assert "model" in body, "model must be in request body"
assert body["model"] == "test-model"
assert "messages" in body, "messages must be in request body"
assert isinstance(body["messages"], list), "messages must be a list"
assert "stream" in body, "stream must be in request body"
@patch("openwebui_cli.commands.chat.create_client")
def test_all_options_with_system_prompt(mock_create_client, mock_config, mock_keyring):
"""Test request options with system prompt included."""
mock_http_client = _create_mock_client()
mock_create_client.return_value = mock_http_client
result = runner.invoke(
app,
[
"chat", "send",
"--model", "gpt-4",
"--no-stream",
"--system", "You are a helpful assistant",
"--chat-id", "sys-chat-789",
"--temperature", "0.8",
"--max-tokens", "3000",
"--prompt", "Hello"
],
)
assert result.exit_code == 0
call_args = mock_http_client.post.call_args
body = call_args.kwargs["json"]
# Check all options
assert body["model"] == "gpt-4"
assert body["chat_id"] == "sys-chat-789"
assert body["temperature"] == 0.8
assert body["max_tokens"] == 3000
# Check system prompt is in messages
assert len(body["messages"]) >= 2
assert body["messages"][0]["role"] == "system"
assert body["messages"][0]["content"] == "You are a helpful assistant"