- vstore 独立安装包(8094):解析/嵌入/加密 vault/faiss 检索,存储路径支持本地 file 与 http(s):// URL,启动握手校验,集群化预留(embed 独立模块) - BFF docgen 路由:上传入库/generate/编辑/导出/下载/主库管理,意图护栏(与前端同词典),vstore 失败兜底,quota 507 与 TTL 惰性清理 - 前端:自然触发意图路由、拖拽上传 chips、能力徽标、右侧文档 TAB(WYSIWYG 编辑/prompt 修改/PDF+DOCX 导出下载)、右栏拖宽+手柄仿左栏 - 批量导入工具 docgen_import.py(并发 8、导入即加密+删源明文) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
83 lines
2.7 KiB
Python
83 lines
2.7 KiB
Python
"""test_backend.py —— 后端路径解析三形态 + 远程握手失败拒绝。"""
|
|
from __future__ import annotations
|
|
|
|
import httpx
|
|
import pytest
|
|
|
|
from vstore.backend import (
|
|
BackendError,
|
|
LocalBackend,
|
|
RemoteBackend,
|
|
handshake,
|
|
is_remote_spec,
|
|
parse_backend,
|
|
)
|
|
|
|
|
|
def test_parse_backend_bare_paths():
|
|
backend = parse_backend("/tmp/vault", "/tmp/index")
|
|
assert isinstance(backend, LocalBackend)
|
|
assert str(backend.vault_path) == "/tmp/vault"
|
|
|
|
|
|
def test_parse_backend_file_url():
|
|
backend = parse_backend("file:///tmp/vault", "file:///tmp/index")
|
|
assert isinstance(backend, LocalBackend)
|
|
assert str(backend.vault_path) == "/tmp/vault" # file:// 前缀剥掉
|
|
assert str(backend.index_path) == "/tmp/index"
|
|
|
|
|
|
def test_parse_backend_http_urls():
|
|
backend = parse_backend("http://h1:1/vault", "https://h2:2/index")
|
|
assert isinstance(backend, RemoteBackend)
|
|
assert backend.vault_url == "http://h1:1/vault"
|
|
assert backend.index_url == "https://h2:2/index"
|
|
|
|
|
|
def test_parse_backend_mixed_rejected():
|
|
with pytest.raises(BackendError):
|
|
parse_backend("/tmp/vault", "http://h:1/index")
|
|
|
|
|
|
def test_is_remote_spec():
|
|
assert is_remote_spec("http://x")
|
|
assert is_remote_spec("https://x")
|
|
assert not is_remote_spec("file:///x")
|
|
assert not is_remote_spec("/tmp/x")
|
|
assert not is_remote_spec("ftp://x") # 仅 http/https 视为远程
|
|
|
|
|
|
def test_local_handshake_ok(tmp_path):
|
|
backend = LocalBackend(str(tmp_path / "vault"), str(tmp_path / "index"))
|
|
handshake(backend) # 不抛
|
|
assert (tmp_path / "vault").exists()
|
|
|
|
|
|
def test_remote_health_ok(monkeypatch):
|
|
def fake_get(url, timeout=None):
|
|
assert url.endswith("/health")
|
|
return httpx.Response(200, json={"ok": True, "count": 0}, request=httpx.Request("GET", url))
|
|
|
|
monkeypatch.setattr("vstore.backend.httpx.get", fake_get)
|
|
backend = RemoteBackend("http://h:1/v", "http://h:2/i")
|
|
assert backend.health()["ok"] is True
|
|
|
|
|
|
def test_remote_handshake_failure_rejected(monkeypatch):
|
|
def fake_get(url, timeout=None):
|
|
raise httpx.ConnectError("down", request=None)
|
|
|
|
monkeypatch.setattr("vstore.backend.httpx.get", fake_get)
|
|
backend = RemoteBackend("http://h:1/v", "http://h:2/i")
|
|
with pytest.raises(BackendError):
|
|
handshake(backend)
|
|
|
|
|
|
def test_remote_search_empty_handshake(monkeypatch):
|
|
def fake_get(url, timeout=None, params=None):
|
|
return httpx.Response(200, json={"ok": True, "mode": "latest", "items": []}, request=httpx.Request("GET", url))
|
|
|
|
monkeypatch.setattr("vstore.backend.httpx.get", fake_get)
|
|
backend = RemoteBackend("http://h:1/v", "http://h:2/i")
|
|
assert backend.search_empty()["ok"] is True
|