Files
2026-07-13 13:22:34 +08:00

204 lines
5.7 KiB
Python

import threading
import time
import pytest
from mlflow.prompt.registry_utils import PromptCache, PromptCacheKey
@pytest.fixture(autouse=True)
def reset_cache():
"""Reset the prompt cache before and after each test."""
PromptCache._reset_instance()
yield
PromptCache._reset_instance()
def test_singleton_pattern():
cache1 = PromptCache.get_instance()
cache2 = PromptCache.get_instance()
assert cache1 is cache2
def test_set_and_get():
cache = PromptCache.get_instance()
key = PromptCacheKey.from_parts("test-prompt", version=1)
cache.set(key, {"template": "Hello {{name}}"})
assert cache.get(key) == {"template": "Hello {{name}}"}
def test_get_nonexistent():
cache = PromptCache.get_instance()
key = PromptCacheKey.from_parts("nonexistent", version=1)
assert cache.get(key) is None
def test_ttl_expiration():
cache = PromptCache.get_instance()
key = PromptCacheKey.from_parts("test-prompt", version=1)
cache.set(key, "value", ttl_seconds=0.01)
time.sleep(0.02)
assert cache.get(key) is None
def test_delete_prompt():
cache = PromptCache.get_instance()
key1 = PromptCacheKey.from_parts("my-prompt", version=1)
key2 = PromptCacheKey.from_parts("my-prompt", version=2)
key3 = PromptCacheKey.from_parts("other-prompt", version=1)
cache.set(key1, "value1")
cache.set(key2, "value2")
cache.set(key3, "value3")
# Delete only version 1 of my-prompt
cache.delete("my-prompt", version=1)
assert cache.get(key1) is None
assert cache.get(key2) == "value2" # version 2 still cached
assert cache.get(key3) == "value3"
def test_delete_prompt_by_alias():
cache = PromptCache.get_instance()
key1 = PromptCacheKey.from_parts("my-prompt", alias="production")
key2 = PromptCacheKey.from_parts("my-prompt", alias="staging")
cache.set(key1, "value1")
cache.set(key2, "value2")
# Delete only the production alias
cache.delete("my-prompt", alias="production")
assert cache.get(key1) is None
assert cache.get(key2) == "value2" # staging still cached
def test_delete_all_prompt_entries():
cache = PromptCache.get_instance()
key1 = PromptCacheKey.from_parts("my-prompt", version=1)
key2 = PromptCacheKey.from_parts("my-prompt", version=2)
key3 = PromptCacheKey.from_parts("my-prompt", alias="latest")
key4 = PromptCacheKey.from_parts("other-prompt", version=1)
cache.set(key1, "value1")
cache.set(key2, "value2")
cache.set(key3, "value3")
cache.set(key4, "value4")
cache.delete_all("my-prompt")
assert cache.get(key1) is None
assert cache.get(key2) is None
assert cache.get(key3) is None
assert cache.get(key4) == "value4"
def test_clear():
cache = PromptCache.get_instance()
key1 = PromptCacheKey.from_parts("prompt1", version=1)
key2 = PromptCacheKey.from_parts("prompt2", version=1)
cache.set(key1, "value1")
cache.set(key2, "value2")
cache.clear()
assert cache.get(key1) is None
assert cache.get(key2) is None
def test_generate_cache_key_with_version():
key = PromptCacheKey.from_parts("my-prompt", version=1)
assert key.name == "my-prompt"
assert key.version == 1
assert key.alias is None
def test_generate_cache_key_with_alias():
key = PromptCacheKey.from_parts("my-prompt", alias="production")
assert key.name == "my-prompt"
assert key.version is None
assert key.alias == "production"
def test_generate_cache_key_with_neither():
key = PromptCacheKey.from_parts("my-prompt")
assert key.name == "my-prompt"
assert key.version is None
assert key.alias is None
def test_generate_cache_key_with_both_raises_error():
with pytest.raises(ValueError, match="Cannot specify both version and alias"):
PromptCacheKey.from_parts("my-prompt", version=1, alias="production")
def test_generate_cache_key_version_zero():
key = PromptCacheKey.from_parts("my-prompt", version=0)
assert key.name == "my-prompt"
assert key.version == 0
assert key.alias is None
def test_concurrent_get_instance():
instances = []
errors = []
def get_instance():
try:
instance = PromptCache.get_instance()
instances.append(instance)
except Exception as e:
errors.append(e)
threads = [
threading.Thread(name=f"prompt-cache-singleton-{i}", target=get_instance) for i in range(10)
]
for t in threads:
t.start()
for t in threads:
t.join()
assert len(errors) == 0
assert all(inst is instances[0] for inst in instances)
def test_concurrent_operations():
cache = PromptCache.get_instance()
errors = []
def writer(thread_id):
try:
for i in range(50):
key = PromptCacheKey.from_parts(f"prompt-{thread_id}-{i}", version=1)
cache.set(key, f"value-{thread_id}-{i}")
except Exception as e:
errors.append(e)
def reader(thread_id):
try:
for i in range(50):
key = PromptCacheKey.from_parts(f"prompt-{thread_id}-{i}", version=1)
cache.get(key)
except Exception as e:
errors.append(e)
threads = []
for i in range(5):
threads.append(threading.Thread(name=f"prompt-cache-writer-{i}", target=writer, args=(i,)))
threads.append(threading.Thread(name=f"prompt-cache-reader-{i}", target=reader, args=(i,)))
for t in threads:
t.start()
for t in threads:
t.join()
assert len(errors) == 0
def test_set_uses_default_ttl():
cache = PromptCache.get_instance()
key = PromptCacheKey.from_parts("test", version=1)
cache.set(key, "value")
assert cache.get(key) == "value"