mirror of
https://github.com/meta-llama/llama-stack.git
synced 2025-07-27 06:28:50 +00:00
feat: use XDG directory standards
Signed-off-by: Mustafa Elbehery <melbeher@redhat.com>
This commit is contained in:
parent
9736f096f6
commit
407c3e3bad
50 changed files with 5611 additions and 508 deletions
422
tests/unit/test_template_xdg_paths.py
Normal file
422
tests/unit/test_template_xdg_paths.py
Normal file
|
@ -0,0 +1,422 @@
|
|||
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
# All rights reserved.
|
||||
#
|
||||
# This source code is licensed under the terms described in the LICENSE file in
|
||||
# the root directory of this source tree.
|
||||
|
||||
import os
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import yaml
|
||||
|
||||
# Template imports will be tested through file system access
|
||||
|
||||
|
||||
class TestTemplateXDGPaths(unittest.TestCase):
|
||||
"""Test that templates use XDG-compliant paths correctly."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test environment."""
|
||||
self.original_env = {}
|
||||
self.env_vars = [
|
||||
"XDG_CONFIG_HOME",
|
||||
"XDG_DATA_HOME",
|
||||
"XDG_STATE_HOME",
|
||||
"XDG_CACHE_HOME",
|
||||
"LLAMA_STACK_CONFIG_DIR",
|
||||
"SQLITE_STORE_DIR",
|
||||
"FILES_STORAGE_DIR",
|
||||
]
|
||||
|
||||
for key in self.env_vars:
|
||||
self.original_env[key] = os.environ.get(key)
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up test environment."""
|
||||
for key, value in self.original_env.items():
|
||||
if value is None:
|
||||
os.environ.pop(key, None)
|
||||
else:
|
||||
os.environ[key] = value
|
||||
|
||||
def clear_env_vars(self):
|
||||
"""Clear all relevant environment variables."""
|
||||
for key in self.env_vars:
|
||||
os.environ.pop(key, None)
|
||||
|
||||
def test_ollama_template_run_yaml_xdg_paths(self):
|
||||
"""Test that ollama template's run.yaml uses XDG environment variables."""
|
||||
template_path = Path(__file__).parent.parent.parent / "llama_stack" / "templates" / "ollama" / "run.yaml"
|
||||
|
||||
if not template_path.exists():
|
||||
self.skipTest("Ollama template not found")
|
||||
|
||||
content = template_path.read_text()
|
||||
|
||||
# Check for XDG-compliant environment variable references
|
||||
self.assertIn("${env.XDG_STATE_HOME:-~/.local/state}", content)
|
||||
self.assertIn("${env.XDG_DATA_HOME:-~/.local/share}", content)
|
||||
|
||||
# Check that paths use llama-stack directory
|
||||
self.assertIn("llama-stack", content)
|
||||
|
||||
# Check specific path patterns
|
||||
self.assertIn("${env.XDG_STATE_HOME:-~/.local/state}/llama-stack/distributions/ollama", content)
|
||||
self.assertIn("${env.XDG_DATA_HOME:-~/.local/share}/llama-stack/distributions/ollama", content)
|
||||
|
||||
def test_ollama_template_run_yaml_parsing(self):
|
||||
"""Test that ollama template's run.yaml can be parsed correctly."""
|
||||
template_path = Path(__file__).parent.parent.parent / "llama_stack" / "templates" / "ollama" / "run.yaml"
|
||||
|
||||
if not template_path.exists():
|
||||
self.skipTest("Ollama template not found")
|
||||
|
||||
content = template_path.read_text()
|
||||
|
||||
# Replace environment variables with test values for parsing
|
||||
test_content = (
|
||||
content.replace("${env.XDG_STATE_HOME:-~/.local/state}", "/test/state")
|
||||
.replace("${env.XDG_DATA_HOME:-~/.local/share}", "/test/data")
|
||||
.replace(
|
||||
"${env.SQLITE_STORE_DIR:=${env.XDG_STATE_HOME:-~/.local/state}/llama-stack/distributions/ollama}",
|
||||
"/test/state/llama-stack/distributions/ollama",
|
||||
)
|
||||
)
|
||||
|
||||
# Should be valid YAML
|
||||
try:
|
||||
yaml.safe_load(test_content)
|
||||
except yaml.YAMLError as e:
|
||||
self.fail(f"Template YAML is invalid: {e}")
|
||||
|
||||
def test_template_environment_variable_expansion(self):
|
||||
"""Test environment variable expansion in templates."""
|
||||
self.clear_env_vars()
|
||||
|
||||
# Set XDG variables
|
||||
os.environ["XDG_STATE_HOME"] = "/custom/state"
|
||||
os.environ["XDG_DATA_HOME"] = "/custom/data"
|
||||
|
||||
# Test pattern that should expand
|
||||
pattern = "${env.XDG_STATE_HOME:-~/.local/state}/llama-stack/test"
|
||||
expected = "/custom/state/llama-stack/test"
|
||||
|
||||
# Mock environment variable expansion (this would normally be done by the shell)
|
||||
expanded = pattern.replace("${env.XDG_STATE_HOME:-~/.local/state}", os.environ["XDG_STATE_HOME"])
|
||||
self.assertEqual(expanded, expected)
|
||||
|
||||
def test_template_fallback_values(self):
|
||||
"""Test that templates have correct fallback values."""
|
||||
self.clear_env_vars()
|
||||
|
||||
# Test fallback pattern
|
||||
pattern = "${env.XDG_STATE_HOME:-~/.local/state}/llama-stack/test"
|
||||
|
||||
# When environment variable is not set, should use fallback
|
||||
if "XDG_STATE_HOME" not in os.environ:
|
||||
# This is what the shell would do
|
||||
expanded = pattern.replace("${env.XDG_STATE_HOME:-~/.local/state}", "~/.local/state")
|
||||
expected = "~/.local/state/llama-stack/test"
|
||||
self.assertEqual(expanded, expected)
|
||||
|
||||
def test_ollama_template_python_config_xdg(self):
|
||||
"""Test that ollama template's Python config uses XDG-compliant paths."""
|
||||
template_path = Path(__file__).parent.parent.parent / "llama_stack" / "templates" / "ollama" / "ollama.py"
|
||||
|
||||
if not template_path.exists():
|
||||
self.skipTest("Ollama template Python file not found")
|
||||
|
||||
content = template_path.read_text()
|
||||
|
||||
# Check for XDG-compliant environment variable references
|
||||
self.assertIn("${env.XDG_STATE_HOME:-~/.local/state}", content)
|
||||
self.assertIn("${env.XDG_DATA_HOME:-~/.local/share}", content)
|
||||
|
||||
# Check that paths use llama-stack directory
|
||||
self.assertIn("llama-stack", content)
|
||||
|
||||
def test_template_path_consistency(self):
|
||||
"""Test that template paths are consistent across different files."""
|
||||
ollama_yaml_path = Path(__file__).parent.parent.parent / "llama_stack" / "templates" / "ollama" / "run.yaml"
|
||||
ollama_py_path = Path(__file__).parent.parent.parent / "llama_stack" / "templates" / "ollama" / "ollama.py"
|
||||
|
||||
if not ollama_yaml_path.exists() or not ollama_py_path.exists():
|
||||
self.skipTest("Ollama template files not found")
|
||||
|
||||
yaml_content = ollama_yaml_path.read_text()
|
||||
py_content = ollama_py_path.read_text()
|
||||
|
||||
# Both should use the same XDG environment variable patterns
|
||||
xdg_patterns = ["${env.XDG_STATE_HOME:-~/.local/state}", "${env.XDG_DATA_HOME:-~/.local/share}", "llama-stack"]
|
||||
|
||||
for pattern in xdg_patterns:
|
||||
self.assertIn(pattern, yaml_content, f"Pattern {pattern} not found in YAML")
|
||||
self.assertIn(pattern, py_content, f"Pattern {pattern} not found in Python")
|
||||
|
||||
def test_template_no_hardcoded_legacy_paths(self):
|
||||
"""Test that templates don't contain hardcoded legacy paths."""
|
||||
template_dir = Path(__file__).parent.parent.parent / "llama_stack" / "templates"
|
||||
|
||||
if not template_dir.exists():
|
||||
self.skipTest("Templates directory not found")
|
||||
|
||||
# Check various template files
|
||||
for template_path in template_dir.rglob("*.yaml"):
|
||||
content = template_path.read_text()
|
||||
|
||||
# Should not contain hardcoded ~/.llama paths
|
||||
self.assertNotIn("~/.llama", content, f"Found hardcoded ~/.llama in {template_path}")
|
||||
|
||||
# Should not contain hardcoded /tmp paths for persistent data
|
||||
if "db_path" in content or "storage_dir" in content:
|
||||
self.assertNotIn("/tmp", content, f"Found hardcoded /tmp in {template_path}")
|
||||
|
||||
def test_template_environment_variable_format(self):
|
||||
"""Test that templates use correct environment variable format."""
|
||||
template_dir = Path(__file__).parent.parent.parent / "llama_stack" / "templates"
|
||||
|
||||
if not template_dir.exists():
|
||||
self.skipTest("Templates directory not found")
|
||||
|
||||
# Pattern for XDG environment variables with fallbacks
|
||||
xdg_patterns = [
|
||||
"${env.XDG_CONFIG_HOME:-~/.config}",
|
||||
"${env.XDG_DATA_HOME:-~/.local/share}",
|
||||
"${env.XDG_STATE_HOME:-~/.local/state}",
|
||||
"${env.XDG_CACHE_HOME:-~/.cache}",
|
||||
]
|
||||
|
||||
for template_path in template_dir.rglob("*.yaml"):
|
||||
content = template_path.read_text()
|
||||
|
||||
# If XDG variables are used, they should have proper fallbacks
|
||||
for pattern in xdg_patterns:
|
||||
base_var = pattern.split(":-")[0] + "}"
|
||||
if base_var in content:
|
||||
self.assertIn(pattern, content, f"XDG variable without fallback in {template_path}")
|
||||
|
||||
def test_template_sqlite_store_dir_xdg(self):
|
||||
"""Test that SQLITE_STORE_DIR uses XDG-compliant fallback."""
|
||||
template_dir = Path(__file__).parent.parent.parent / "llama_stack" / "templates"
|
||||
|
||||
if not template_dir.exists():
|
||||
self.skipTest("Templates directory not found")
|
||||
|
||||
for template_path in template_dir.rglob("*.yaml"):
|
||||
content = template_path.read_text()
|
||||
|
||||
if "SQLITE_STORE_DIR" in content:
|
||||
# Should use XDG fallback pattern
|
||||
self.assertIn("${env.XDG_STATE_HOME:-~/.local/state}", content)
|
||||
self.assertIn("llama-stack", content)
|
||||
|
||||
def test_template_files_storage_dir_xdg(self):
|
||||
"""Test that FILES_STORAGE_DIR uses XDG-compliant fallback."""
|
||||
template_dir = Path(__file__).parent.parent.parent / "llama_stack" / "templates"
|
||||
|
||||
if not template_dir.exists():
|
||||
self.skipTest("Templates directory not found")
|
||||
|
||||
for template_path in template_dir.rglob("*.yaml"):
|
||||
content = template_path.read_text()
|
||||
|
||||
if "FILES_STORAGE_DIR" in content:
|
||||
# Should use XDG fallback pattern
|
||||
self.assertIn("${env.XDG_DATA_HOME:-~/.local/share}", content)
|
||||
self.assertIn("llama-stack", content)
|
||||
|
||||
|
||||
class TestTemplateCodeGeneration(unittest.TestCase):
|
||||
"""Test template code generation with XDG paths."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test environment."""
|
||||
self.original_env = {}
|
||||
for key in ["XDG_CONFIG_HOME", "XDG_DATA_HOME", "XDG_STATE_HOME", "LLAMA_STACK_CONFIG_DIR"]:
|
||||
self.original_env[key] = os.environ.get(key)
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up test environment."""
|
||||
for key, value in self.original_env.items():
|
||||
if value is None:
|
||||
os.environ.pop(key, None)
|
||||
else:
|
||||
os.environ[key] = value
|
||||
|
||||
def test_provider_codegen_xdg_paths(self):
|
||||
"""Test that provider code generation uses XDG-compliant paths."""
|
||||
codegen_path = Path(__file__).parent.parent.parent / "scripts" / "provider_codegen.py"
|
||||
|
||||
if not codegen_path.exists():
|
||||
self.skipTest("Provider codegen script not found")
|
||||
|
||||
content = codegen_path.read_text()
|
||||
|
||||
# Should use XDG-compliant path in documentation
|
||||
self.assertIn("${env.XDG_DATA_HOME:-~/.local/share}/llama-stack", content)
|
||||
|
||||
# Should not use hardcoded ~/.llama paths
|
||||
self.assertNotIn("~/.llama/dummy", content)
|
||||
|
||||
def test_template_sample_config_paths(self):
|
||||
"""Test that template sample configs use XDG-compliant paths."""
|
||||
# This test checks that when templates generate sample configs,
|
||||
# they use XDG-compliant paths
|
||||
|
||||
# Mock a template that generates sample config
|
||||
with patch("llama_stack.templates.template.Template") as mock_template:
|
||||
mock_instance = MagicMock()
|
||||
mock_template.return_value = mock_instance
|
||||
|
||||
# Mock sample config generation
|
||||
def mock_sample_config(distro_dir):
|
||||
# Should use XDG-compliant path structure
|
||||
self.assertIn("llama-stack", distro_dir)
|
||||
return {"config": "test"}
|
||||
|
||||
mock_instance.sample_run_config = mock_sample_config
|
||||
|
||||
# Test sample config generation
|
||||
template = mock_template()
|
||||
template.sample_run_config("${env.XDG_DATA_HOME:-~/.local/share}/llama-stack/test")
|
||||
|
||||
def test_template_path_substitution(self):
|
||||
"""Test that template path substitution works correctly."""
|
||||
# Test path substitution in template generation
|
||||
|
||||
original_path = "~/.llama/distributions/test"
|
||||
|
||||
# Should be converted to XDG-compliant path
|
||||
xdg_path = original_path.replace("~/.llama", "${env.XDG_DATA_HOME:-~/.local/share}/llama-stack")
|
||||
expected = "${env.XDG_DATA_HOME:-~/.local/share}/llama-stack/distributions/test"
|
||||
|
||||
self.assertEqual(xdg_path, expected)
|
||||
|
||||
def test_template_environment_variable_precedence(self):
|
||||
"""Test environment variable precedence in templates."""
|
||||
# Test that custom XDG variables take precedence over defaults
|
||||
|
||||
test_cases = [
|
||||
{
|
||||
"env": {"XDG_STATE_HOME": "/custom/state"},
|
||||
"pattern": "${env.XDG_STATE_HOME:-~/.local/state}/llama-stack/test",
|
||||
"expected": "/custom/state/llama-stack/test",
|
||||
},
|
||||
{
|
||||
"env": {}, # No XDG variable set
|
||||
"pattern": "${env.XDG_STATE_HOME:-~/.local/state}/llama-stack/test",
|
||||
"expected": "~/.local/state/llama-stack/test",
|
||||
},
|
||||
]
|
||||
|
||||
for case in test_cases:
|
||||
# Clear environment
|
||||
for key in ["XDG_STATE_HOME", "XDG_DATA_HOME", "XDG_CONFIG_HOME"]:
|
||||
os.environ.pop(key, None)
|
||||
|
||||
# Set test environment
|
||||
for key, value in case["env"].items():
|
||||
os.environ[key] = value
|
||||
|
||||
# Simulate shell variable expansion
|
||||
pattern = case["pattern"]
|
||||
for key, value in case["env"].items():
|
||||
var_pattern = f"${{env.{key}:-"
|
||||
if var_pattern in pattern:
|
||||
# Replace with actual value
|
||||
pattern = pattern.replace(f"${{env.{key}:-~/.local/state}}", value)
|
||||
|
||||
# If no replacement happened, use fallback
|
||||
if "${env.XDG_STATE_HOME:-~/.local/state}" in pattern:
|
||||
pattern = pattern.replace("${env.XDG_STATE_HOME:-~/.local/state}", "~/.local/state")
|
||||
|
||||
self.assertEqual(pattern, case["expected"])
|
||||
|
||||
|
||||
class TestTemplateIntegration(unittest.TestCase):
|
||||
"""Integration tests for templates with XDG compliance."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test environment."""
|
||||
self.original_env = {}
|
||||
for key in ["XDG_CONFIG_HOME", "XDG_DATA_HOME", "XDG_STATE_HOME", "LLAMA_STACK_CONFIG_DIR"]:
|
||||
self.original_env[key] = os.environ.get(key)
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up test environment."""
|
||||
for key, value in self.original_env.items():
|
||||
if value is None:
|
||||
os.environ.pop(key, None)
|
||||
else:
|
||||
os.environ[key] = value
|
||||
|
||||
def test_template_with_xdg_environment(self):
|
||||
"""Test template behavior with XDG environment variables set."""
|
||||
# Clear environment
|
||||
for key in self.original_env:
|
||||
os.environ.pop(key, None)
|
||||
|
||||
# Set custom XDG variables
|
||||
os.environ["XDG_CONFIG_HOME"] = "/custom/config"
|
||||
os.environ["XDG_DATA_HOME"] = "/custom/data"
|
||||
os.environ["XDG_STATE_HOME"] = "/custom/state"
|
||||
|
||||
# Test that template paths would resolve correctly
|
||||
# (This is a conceptual test since actual shell expansion happens at runtime)
|
||||
|
||||
template_pattern = "${env.XDG_STATE_HOME:-~/.local/state}/llama-stack/test"
|
||||
|
||||
# In a real shell, this would expand to:
|
||||
|
||||
# Verify the pattern structure is correct
|
||||
self.assertIn("XDG_STATE_HOME", template_pattern)
|
||||
self.assertIn("llama-stack", template_pattern)
|
||||
self.assertIn("~/.local/state", template_pattern) # fallback
|
||||
|
||||
def test_template_with_no_xdg_environment(self):
|
||||
"""Test template behavior with no XDG environment variables."""
|
||||
# Clear all XDG environment variables
|
||||
for key in ["XDG_CONFIG_HOME", "XDG_DATA_HOME", "XDG_STATE_HOME", "XDG_CACHE_HOME"]:
|
||||
os.environ.pop(key, None)
|
||||
|
||||
# Test that templates would use fallback values
|
||||
template_pattern = "${env.XDG_STATE_HOME:-~/.local/state}/llama-stack/test"
|
||||
|
||||
# In a real shell with no XDG_STATE_HOME, this would expand to:
|
||||
|
||||
# Verify the pattern structure includes fallback
|
||||
self.assertIn(":-~/.local/state", template_pattern)
|
||||
|
||||
def test_template_consistency_across_providers(self):
|
||||
"""Test that all template providers use consistent XDG patterns."""
|
||||
templates_dir = Path(__file__).parent.parent.parent / "llama_stack" / "templates"
|
||||
|
||||
if not templates_dir.exists():
|
||||
self.skipTest("Templates directory not found")
|
||||
|
||||
# Expected XDG patterns that should be consistent across templates
|
||||
|
||||
# Check a few different provider templates
|
||||
provider_templates = []
|
||||
for provider_dir in templates_dir.iterdir():
|
||||
if provider_dir.is_dir() and not provider_dir.name.startswith("."):
|
||||
run_yaml = provider_dir / "run.yaml"
|
||||
if run_yaml.exists():
|
||||
provider_templates.append(run_yaml)
|
||||
|
||||
if not provider_templates:
|
||||
self.skipTest("No provider templates found")
|
||||
|
||||
# Check that templates use consistent patterns
|
||||
for template_path in provider_templates[:3]: # Check first 3 templates
|
||||
content = template_path.read_text()
|
||||
|
||||
# Should use llama-stack in paths
|
||||
if any(xdg_var in content for xdg_var in ["XDG_CONFIG_HOME", "XDG_DATA_HOME", "XDG_STATE_HOME"]):
|
||||
self.assertIn("llama-stack", content, f"Template {template_path} uses XDG but not llama-stack")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
Loading…
Add table
Add a link
Reference in a new issue