Creator App Tests / PR Spam Gate (push) Skipped
Frontend Tests / PR Spam Gate (push) Skipped
NPM Format (website & console) / PR Spam Gate (push) Skipped
Pre-commit Checks / PR Spam Gate (push) Skipped
QwenPaw Creator Plugin Release / check-version (push) Canceled after 0s
QwenPaw Creator Plugin Release / upload-oss (push) Canceled after 0s
Tests / PR Spam Gate (push) Skipped
CodeQL / CodeQL (python) (push) Canceled after 0s
CodeQL / CodeQL (typescript) (push) Canceled after 0s
Creator App Tests / Creator Backend Tests (push) Canceled after 0s
Creator App Tests / Creator UI Build & Tests (push) Canceled after 0s
E2E Smoke Tests / Playwright UI Smoke (push) Canceled after 0s
Frontend Tests / Detect console changes (push) Canceled after 0s
NPM Format (website & console) / website format check (push) Canceled after 0s
NPM Format (website & console) / console format check (push) Canceled after 0s
Pre-commit Checks / Pre-commit (push) Canceled after 0s
QwenPaw Data Plugin Release / check-version (push) Canceled after 0s
Tests / Detect code changes (push) Canceled after 0s
Frontend Tests / Vitest Unit Tests (push) Canceled after 0s
Frontend Tests / Frontend Summary (push) Canceled after 0s
QwenPaw Data Plugin Release / upload-oss (push) Canceled after 0s
Tests / Maintainer Approval (push) Canceled after 0s
Tests / Unit Tests - py3.11 - macos-latest (push) Canceled after 0s
Tests / Unit Tests - py3.11 - ubuntu-latest (push) Canceled after 0s
Tests / Unit Tests - py3.13 - ubuntu-latest (push) Canceled after 0s
Tests / Unit Tests - py3.11 - windows-latest (push) Canceled after 0s
Tests / Contract Tests - py3.11 - macos-latest (push) Canceled after 0s
Tests / Contract Tests - py3.11 - ubuntu-latest (push) Canceled after 0s
Tests / Contract Tests - py3.13 - ubuntu-latest (push) Canceled after 0s
Tests / Contract Tests - py3.11 - windows-latest (push) Canceled after 0s
Tests / Integrated Tests - py3.11 - macos-latest - p0 (push) Canceled after 0s
Tests / Integrated Tests - py3.11 - macos-latest - p1 (push) Canceled after 0s
Tests / Integrated Tests - py3.11 - macos-latest - p2 (push) Canceled after 0s
Tests / Integrated Tests - py3.11 - ubuntu-latest - fallback (push) Canceled after 0s
Tests / Integrated Tests - py3.11 - ubuntu-latest - p0 (push) Canceled after 0s
Tests / Integrated Tests - py3.11 - ubuntu-latest - p1 (push) Canceled after 0s
Tests / Integrated Tests - py3.11 - ubuntu-latest - p2 (push) Canceled after 0s
Tests / Integrated Tests - py3.13 - ubuntu-latest - p0 (push) Canceled after 0s
Tests / Integrated Tests - py3.13 - ubuntu-latest - p1 (push) Canceled after 0s
Tests / Integrated Tests - py3.13 - ubuntu-latest - p2 (push) Canceled after 0s
Tests / Integrated Tests - py3.11 - windows-latest - p0 (push) Canceled after 0s
Tests / Integrated Tests - py3.11 - windows-latest - p1 (push) Canceled after 0s
Tests / Integrated Tests - py3.11 - windows-latest - p2 (push) Canceled after 0s
Tests / Coverage Report (push) Canceled after 0s
Tests / Test Summary (push) Canceled after 0s
117 lines
3.8 KiB
Python
117 lines
3.8 KiB
Python
# -*- coding: utf-8 -*-
|
|
# pylint: disable=protected-access,redefined-outer-name
|
|
from __future__ import annotations
|
|
|
|
import time
|
|
from unittest import mock
|
|
|
|
import pytest
|
|
|
|
from qwenpaw.providers.model_capability_cache import ModelCapabilityCache
|
|
|
|
_TTL = "qwenpaw.providers.model_capability_cache.CAPABILITY_CACHE_TTL_SECONDS"
|
|
|
|
|
|
@pytest.fixture()
|
|
def cache() -> ModelCapabilityCache:
|
|
return ModelCapabilityCache()
|
|
|
|
|
|
def test_learn_and_get_roundtrip(cache: ModelCapabilityCache) -> None:
|
|
cache.learn("p:m", "rejects_media", True)
|
|
assert cache.get("p:m", "rejects_media", False) is True
|
|
|
|
|
|
def test_get_returns_default_when_unlearned(
|
|
cache: ModelCapabilityCache,
|
|
) -> None:
|
|
assert cache.get("p:m", "rejects_media", False) is False
|
|
assert cache.get("p:m", "rejects_media") is None
|
|
|
|
|
|
def test_clear_all(cache: ModelCapabilityCache) -> None:
|
|
cache.learn("p:m", "rejects_media", True)
|
|
cache.clear()
|
|
assert cache.get("p:m", "rejects_media", False) is False
|
|
|
|
|
|
def test_clear_single_model_key(cache: ModelCapabilityCache) -> None:
|
|
cache.learn("a:m", "rejects_media", True)
|
|
cache.learn("b:n", "rejects_media", True)
|
|
cache.clear("a:m")
|
|
assert cache.get("a:m", "rejects_media", False) is False
|
|
assert cache.get("b:n", "rejects_media", False) is True
|
|
|
|
|
|
def test_forget_drops_single_capability(cache: ModelCapabilityCache) -> None:
|
|
cache.learn("p:m", "rejects_media", True)
|
|
cache.learn("p:m", "needs_reasoning_content", True)
|
|
cache.forget("p:m", "rejects_media")
|
|
assert cache.get("p:m", "rejects_media", False) is False
|
|
assert cache.get("p:m", "needs_reasoning_content", False) is True
|
|
|
|
|
|
def test_forget_preserves_other_models(cache: ModelCapabilityCache) -> None:
|
|
cache.learn("a:m", "rejects_media", True)
|
|
cache.learn("b:n", "rejects_media", True)
|
|
cache.forget("a:m", "rejects_media")
|
|
assert cache.get("a:m", "rejects_media", False) is False
|
|
assert cache.get("b:n", "rejects_media", False) is True
|
|
|
|
|
|
def test_forget_unknown_key_is_noop(cache: ModelCapabilityCache) -> None:
|
|
cache.forget("nonexistent", "rejects_media")
|
|
cache.learn("p:m", "rejects_media", True)
|
|
cache.forget("p:m", "needs_reasoning_content")
|
|
assert cache.get("p:m", "rejects_media", False) is True
|
|
|
|
|
|
def test_relearn_same_value_refreshes_timestamp(
|
|
cache: ModelCapabilityCache,
|
|
) -> None:
|
|
with mock.patch(
|
|
"qwenpaw.providers.model_capability_cache.time.monotonic",
|
|
side_effect=[100.0, 200.0],
|
|
):
|
|
cache.learn("p:m", "rejects_media", True)
|
|
set_at_before = cache._learned["p:m"]["rejects_media"].set_at
|
|
cache.learn("p:m", "rejects_media", True)
|
|
set_at_after = cache._learned["p:m"]["rejects_media"].set_at
|
|
assert set_at_after > set_at_before
|
|
assert cache._learned["p:m"]["rejects_media"].value is True
|
|
|
|
|
|
def test_relearn_different_value_overwrites(
|
|
cache: ModelCapabilityCache,
|
|
) -> None:
|
|
cache.learn("p:m", "rejects_media", True)
|
|
cache.learn("p:m", "rejects_media", False)
|
|
assert cache.get("p:m", "rejects_media") is False
|
|
|
|
|
|
def test_expired_entry_returns_default(
|
|
cache: ModelCapabilityCache,
|
|
) -> None:
|
|
with mock.patch(_TTL, 0.05):
|
|
cache.learn("p:m", "rejects_media", True)
|
|
assert cache.get("p:m", "rejects_media", False) is True
|
|
time.sleep(0.08)
|
|
assert cache.get("p:m", "rejects_media", False) is False
|
|
|
|
|
|
def test_expired_entry_is_evicted_from_bucket(
|
|
cache: ModelCapabilityCache,
|
|
) -> None:
|
|
with mock.patch(_TTL, 0.05):
|
|
cache.learn("p:m", "rejects_media", True)
|
|
time.sleep(0.08)
|
|
cache.get("p:m", "rejects_media", False)
|
|
assert "p:m" not in cache._learned
|
|
|
|
|
|
def test_ttl_zero_disables_expiry(cache: ModelCapabilityCache) -> None:
|
|
with mock.patch(_TTL, 0.0):
|
|
cache.learn("p:m", "rejects_media", True)
|
|
time.sleep(0.05)
|
|
assert cache.get("p:m", "rejects_media", False) is True
|