test: add multiple new test cases and update python version
1. update .python-version from 3.11 to 3.13 2. add tests for IS_RUNNING and DIR_EXISTS conditions 3. add graph-related tests including string ref parsing, mermaid output, GraphComposer and compose function 4. add storage backend TTL tests for both MemoryBackend and JSONBackend 5. add new system task tests for clr, reset_icon_cache, setenv and which 6. add comprehensive task spec tests including soft dependencies, retry policy, context managers and task template 7. add executor edge case tests for various scenarios
This commit is contained in:
@@ -5,6 +5,7 @@ from __future__ import annotations
|
||||
import json
|
||||
import os
|
||||
import tempfile
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
@@ -43,6 +44,46 @@ def test_memory_backend_get_missing_raises() -> None:
|
||||
b.get("nope")
|
||||
|
||||
|
||||
def test_memory_backend_ttl_expired() -> None:
|
||||
"""MemoryBackend TTL 过期后 has/get 返回 False/抛 KeyError."""
|
||||
b = MemoryBackend(ttl=0.1) # 0.1 秒过期
|
||||
b.save("a", 1)
|
||||
assert b.has("a")
|
||||
time.sleep(0.15)
|
||||
assert not b.has("a")
|
||||
with pytest.raises(KeyError):
|
||||
b.get("a")
|
||||
|
||||
|
||||
def test_memory_backend_ttl_load_filters_expired() -> None:
|
||||
"""MemoryBackend.load() 应过滤过期的条目."""
|
||||
b = MemoryBackend(ttl=0.1)
|
||||
b.save("a", 1)
|
||||
b.save("b", 2)
|
||||
time.sleep(0.15)
|
||||
# a 过期,但 b 也要过期... 需要更精确控制
|
||||
# 使用 monkeypatch 更可控
|
||||
b._store["expired"] = ("value", time.monotonic() - 100) # 手动设置过期时间
|
||||
b._store["fresh"] = ("value2", time.monotonic())
|
||||
assert "expired" not in dict(b.load())
|
||||
assert "fresh" in dict(b.load())
|
||||
|
||||
|
||||
def test_memory_backend_expired_key_not_in_store() -> None:
|
||||
"""_expired 对不存在键返回 False."""
|
||||
b = MemoryBackend(ttl=1.0)
|
||||
assert b._expired("nonexistent") is False
|
||||
|
||||
|
||||
def test_memory_backend_no_ttl_never_expired() -> None:
|
||||
"""无 TTL 时永不过期."""
|
||||
b = MemoryBackend()
|
||||
b.save("a", 1)
|
||||
b._store["a"] = (1, time.monotonic() - 1000) # 手动设置很久以前的存储
|
||||
assert b.has("a") # 仍然存在
|
||||
assert b.get("a") == 1
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------- #
|
||||
# JSONBackend
|
||||
# ---------------------------------------------------------------------- #
|
||||
@@ -150,6 +191,94 @@ def test_json_backend_non_dict_content_ignored(tmp_path: Path) -> None:
|
||||
assert dict(b.load()) == {}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------- #
|
||||
# JSONBackend TTL 测试
|
||||
# ---------------------------------------------------------------------- #
|
||||
def test_json_backend_ttl_expired_has_returns_false() -> None:
|
||||
"""JSONBackend TTL 过期后 has 返回 False."""
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
path = str(Path(tmp) / "state.json")
|
||||
b = JSONBackend(path, ttl=0.1)
|
||||
b.save("a", 1)
|
||||
assert b.has("a")
|
||||
time.sleep(0.15)
|
||||
assert not b.has("a")
|
||||
|
||||
|
||||
def test_json_backend_ttl_expired_get_raises_keyerror() -> None:
|
||||
"""JSONBackend TTL 过期后 get 抛 KeyError."""
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
path = str(Path(tmp) / "state.json")
|
||||
b = JSONBackend(path, ttl=0.1)
|
||||
b.save("a", 1)
|
||||
time.sleep(0.15)
|
||||
with pytest.raises(KeyError):
|
||||
b.get("a")
|
||||
|
||||
|
||||
def test_json_backend_ttl_load_filters_expired() -> None:
|
||||
"""JSONBackend.load() 应过滤过期的条目."""
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
path = str(Path(tmp) / "state.json")
|
||||
b = JSONBackend(path, ttl=0.1)
|
||||
b.save("a", 1)
|
||||
b.save("b", 2)
|
||||
time.sleep(0.15)
|
||||
# 两个都过期了
|
||||
assert dict(b.load()) == {}
|
||||
|
||||
|
||||
def test_json_backend_expired_no_ttl() -> None:
|
||||
"""无 TTL 时 _expired 返回 False."""
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
path = str(Path(tmp) / "state.json")
|
||||
b = JSONBackend(path)
|
||||
b.save("a", 1)
|
||||
# 手动修改 ts 为很久以前
|
||||
b._store["a"]["ts"] = time.time() - 1000
|
||||
assert b._expired(b._store["a"]) is False # 无 TTL,永不过期
|
||||
|
||||
|
||||
def test_json_backend_expired_with_ttl() -> None:
|
||||
"""有 TTL 时 _expired 检查是否过期."""
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
path = str(Path(tmp) / "state.json")
|
||||
b = JSONBackend(path, ttl=1.0)
|
||||
b.save("a", 1)
|
||||
# 手动修改 ts 为很久以前
|
||||
b._store["a"]["ts"] = time.time() - 10 # 10 秒前,超过 TTL
|
||||
assert b._expired(b._store["a"]) is True
|
||||
|
||||
|
||||
def test_json_backend_expired_missing_ts() -> None:
|
||||
"""entry 缺少 ts 时使用默认值 0."""
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
path = str(Path(tmp) / "state.json")
|
||||
b = JSONBackend(path, ttl=1.0)
|
||||
b._store["a"] = {"value": 1} # 缺少 ts
|
||||
# ts 默认为 0,已经过了很久
|
||||
assert b._expired(b._store["a"]) is True
|
||||
|
||||
|
||||
def test_json_backend_save_value_error(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""save 时 json.dumps 抛 ValueError 应转为 StorageError."""
|
||||
import json as _json
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
path = str(Path(tmp) / "state.json")
|
||||
b = JSONBackend(path)
|
||||
|
||||
original_dumps = _json.dumps
|
||||
|
||||
def flaky_dumps(*_args: Any, **_kwargs: Any) -> str:
|
||||
raise ValueError("simulated dumps failure")
|
||||
|
||||
monkeypatch.setattr(_json, "dumps", flaky_dumps)
|
||||
with pytest.raises(StorageError, match="not JSON-serialisable"):
|
||||
b.save("a", 1)
|
||||
monkeypatch.setattr(_json, "dumps", original_dumps)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------- #
|
||||
# resolve_backend
|
||||
# ---------------------------------------------------------------------- #
|
||||
|
||||
Reference in New Issue
Block a user