chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,307 @@
|
||||
# Copyright 2025-present the zvec project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import logging
|
||||
import pytest
|
||||
import tempfile
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
import zvec
|
||||
import zvec
|
||||
from zvec import LogType, LogLevel
|
||||
|
||||
# Error messages
|
||||
INITIALIZATION_ERROR_MSG = "initialization failed"
|
||||
RUNTIME_ERROR_MSG = "RuntimeError"
|
||||
VALUE_ERROR_MSG = "ValueError"
|
||||
TYPE_ERROR_MSG = "TypeError"
|
||||
|
||||
|
||||
# ==================== helper ====================
|
||||
def run_in_subprocess(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
if os.getenv("RUNNING_IN_SUBPROCESS"):
|
||||
return func(*args, **kwargs)
|
||||
|
||||
env = os.environ.copy()
|
||||
env["RUNNING_IN_SUBPROCESS"] = "1"
|
||||
env["PYTEST_CURRENT_TEST"] = func.__name__
|
||||
|
||||
import inspect
|
||||
|
||||
filepath = inspect.getfile(func)
|
||||
qualname = func.__qualname__.replace(".", "::")
|
||||
test_id = f"{filepath}::{qualname}"
|
||||
|
||||
project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
env["PYTHONPATH"] = project_root + ":" + env.get("PYTHONPATH", "")
|
||||
|
||||
cmd = [sys.executable, "-m", "pytest", "-v", "-s", test_id]
|
||||
|
||||
result = subprocess.run(cmd, env=env, capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
pytest.fail(
|
||||
f"Subprocess test {func.__name__} failed with code {result.returncode}\n"
|
||||
f"STDOUT:\n{result.stdout}\nSTDERR:\n{result.stderr}"
|
||||
)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
# ==================== Fixtures ====================
|
||||
@pytest.fixture(scope="function")
|
||||
def temp_log_dir(tmp_path_factory):
|
||||
return tmp_path_factory.mktemp("logs")
|
||||
|
||||
|
||||
# ==================== Tests ====================
|
||||
class TestDbConfigInitialization:
|
||||
@run_in_subprocess
|
||||
def test_init_default(self):
|
||||
# default config
|
||||
# log_type: Optional[LogType] = LogType.CONSOLE,
|
||||
# log_level: Optional[LogLevel] = LogLevel.WARN,
|
||||
# log_dir: Optional[str] = "./logs",
|
||||
# log_basename: Optional[str] = "zvec.log",
|
||||
# log_file_size: Optional[int] = 2048,
|
||||
# log_overdue_days: Optional[int] = 7,
|
||||
zvec.init()
|
||||
|
||||
@run_in_subprocess
|
||||
def test_init_file_logger(self):
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
|
||||
zvec.init(
|
||||
log_level=LogLevel.DEBUG,
|
||||
log_type=LogType.FILE,
|
||||
)
|
||||
# assert logdir exist
|
||||
log_dir = Path("./logs")
|
||||
assert log_dir.exists()
|
||||
|
||||
# validate write log
|
||||
col = zvec.create_and_open(
|
||||
"/tmp/test/1",
|
||||
zvec.CollectionSchema(
|
||||
name="test",
|
||||
vectors=zvec.VectorSchema(
|
||||
dimension=4,
|
||||
data_type=zvec.DataType.VECTOR_FP32,
|
||||
name="image",
|
||||
),
|
||||
),
|
||||
)
|
||||
col.insert(docs=[zvec.Doc(id="1", vectors={"image": [1.0, 2.0, 3.0, 4.0]})])
|
||||
assert any(log_dir.glob("zvec.log.*"))
|
||||
|
||||
# clear
|
||||
col.destroy()
|
||||
shutil.rmtree(log_dir, ignore_errors=True)
|
||||
|
||||
@run_in_subprocess
|
||||
def test_init_with_mixed_config(self):
|
||||
zvec.init(
|
||||
memory_limit_mb=128,
|
||||
log_type=LogType.FILE,
|
||||
query_threads=1,
|
||||
log_level=LogLevel.WARN,
|
||||
)
|
||||
|
||||
@run_in_subprocess
|
||||
def test_repeated_initialization(self):
|
||||
# Calling init() repeatedly is allowed:
|
||||
# it succeeds but becomes a no-op after the first successful init()
|
||||
zvec.init()
|
||||
|
||||
|
||||
class TestDbConfigMemoryLimitValidation:
|
||||
@run_in_subprocess
|
||||
def test_memory_limit_min_valid(self):
|
||||
# MIN_MEMORY_LIMIT_BYTES is 100M
|
||||
with pytest.raises(RuntimeError):
|
||||
zvec.init(memory_limit_mb=99)
|
||||
|
||||
@run_in_subprocess
|
||||
def test_memory_limit_invalid_value(self):
|
||||
# memory_limit_mb must >= 0 and must be int and if None, set default value
|
||||
with pytest.raises(ValueError):
|
||||
zvec.init(memory_limit_mb=0)
|
||||
with pytest.raises(ValueError):
|
||||
zvec.init(memory_limit_mb=-1)
|
||||
with pytest.raises(TypeError):
|
||||
zvec.init(memory_limit_mb="512")
|
||||
with pytest.raises(TypeError):
|
||||
zvec.init(memory_limit_mb=512.5)
|
||||
|
||||
|
||||
class TestDbConfigThreadValidation:
|
||||
@run_in_subprocess
|
||||
def test_query_threads(self):
|
||||
zvec.init(query_threads=1)
|
||||
|
||||
@run_in_subprocess
|
||||
def test_query_threads_invalid(self):
|
||||
# query_threads must >= 0 and must be int and if None, set default value
|
||||
with pytest.raises(ValueError):
|
||||
zvec.init(query_threads=0)
|
||||
with pytest.raises(ValueError):
|
||||
zvec.init(query_threads=-1)
|
||||
with pytest.raises(TypeError):
|
||||
zvec.init(query_threads="value")
|
||||
with pytest.raises(TypeError):
|
||||
zvec.init(query_threads=512.5)
|
||||
with pytest.raises(TypeError):
|
||||
zvec.init(query_threads="512")
|
||||
|
||||
@run_in_subprocess
|
||||
def test_optimize_threads(self):
|
||||
zvec.init(optimize_threads=1)
|
||||
|
||||
@run_in_subprocess
|
||||
def test_optimize_threads_invalid(self):
|
||||
# optimize_threads must >= 0 and must be int and if None, set default value
|
||||
with pytest.raises(ValueError):
|
||||
zvec.init(optimize_threads=0)
|
||||
with pytest.raises(ValueError):
|
||||
zvec.init(optimize_threads=-1)
|
||||
with pytest.raises(TypeError):
|
||||
zvec.init(optimize_threads="value")
|
||||
with pytest.raises(TypeError):
|
||||
zvec.init(optimize_threads=512.5)
|
||||
with pytest.raises(TypeError):
|
||||
zvec.init(optimize_threads="512")
|
||||
|
||||
|
||||
class TestDbConfigRatioValidation:
|
||||
@run_in_subprocess
|
||||
def test_init_invert_to_forward_scan_ratio(self):
|
||||
# must be in [0,1]
|
||||
zvec.init(invert_to_forward_scan_ratio=0.8)
|
||||
|
||||
@run_in_subprocess
|
||||
def test_init_invert_to_forward_scan_ratio_invalid(self):
|
||||
with pytest.raises(ValueError):
|
||||
zvec.init(invert_to_forward_scan_ratio=1.1)
|
||||
with pytest.raises(ValueError):
|
||||
zvec.init(invert_to_forward_scan_ratio=-0.1)
|
||||
with pytest.raises(TypeError):
|
||||
zvec.init(invert_to_forward_scan_ratio="0.8")
|
||||
|
||||
@run_in_subprocess
|
||||
def test_init_brute_force_by_keys_ratio(self):
|
||||
zvec.init(brute_force_by_keys_ratio=0.8)
|
||||
|
||||
@run_in_subprocess
|
||||
def test_init_brute_force_by_keys_ratio_invalid(self):
|
||||
with pytest.raises(ValueError):
|
||||
zvec.init(brute_force_by_keys_ratio=1.1)
|
||||
with pytest.raises(ValueError):
|
||||
zvec.init(brute_force_by_keys_ratio=-0.1)
|
||||
with pytest.raises(TypeError):
|
||||
zvec.init(brute_force_by_keys_ratio="0.8")
|
||||
|
||||
|
||||
class TestDbConfigLogValidation:
|
||||
@run_in_subprocess
|
||||
def test_log_type_valid(self):
|
||||
zvec.init(log_type=LogType.CONSOLE)
|
||||
|
||||
@run_in_subprocess
|
||||
def test_log_type_invalid(self):
|
||||
with pytest.raises(TypeError):
|
||||
zvec.init(log_type="FILE")
|
||||
with pytest.raises(TypeError):
|
||||
zvec.init(log_type="")
|
||||
with pytest.raises(TypeError):
|
||||
zvec.init(log_type="invalid")
|
||||
with pytest.raises(TypeError):
|
||||
zvec.init(log_type=123)
|
||||
|
||||
@run_in_subprocess
|
||||
def test_log_level_valid(self):
|
||||
zvec.init(log_level=LogLevel.ERROR)
|
||||
|
||||
@run_in_subprocess
|
||||
def test_log_level_invalid(self):
|
||||
with pytest.raises(TypeError):
|
||||
zvec.init(log_level="WARN")
|
||||
with pytest.raises(TypeError):
|
||||
zvec.init(log_level="")
|
||||
with pytest.raises(TypeError):
|
||||
zvec.init(log_level="invalid")
|
||||
with pytest.raises(TypeError):
|
||||
zvec.init(log_level=123)
|
||||
|
||||
@run_in_subprocess
|
||||
def test_init_file_logger(self):
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
|
||||
temp_dir = tempfile.mkdtemp(prefix="log_test_")
|
||||
abs_temp_dir = os.path.abspath(temp_dir)
|
||||
|
||||
zvec.init(
|
||||
log_level=LogLevel.DEBUG,
|
||||
log_type=LogType.FILE,
|
||||
log_dir=abs_temp_dir,
|
||||
log_basename="test",
|
||||
)
|
||||
|
||||
# assert logdir exist
|
||||
log_dir = Path(abs_temp_dir)
|
||||
assert log_dir.exists()
|
||||
|
||||
# validate write log
|
||||
col = zvec.create_and_open(
|
||||
"/tmp/test/1",
|
||||
zvec.CollectionSchema(
|
||||
name="test",
|
||||
vectors=zvec.VectorSchema(
|
||||
dimension=4,
|
||||
data_type=zvec.DataType.VECTOR_FP32,
|
||||
name="image",
|
||||
),
|
||||
),
|
||||
)
|
||||
col.insert(docs=[zvec.Doc(id="1", vectors={"image": [1.0, 2.0, 3.0, 4.0]})])
|
||||
assert any(log_dir.glob("test.*"))
|
||||
|
||||
# clear
|
||||
col.destroy()
|
||||
shutil.rmtree(log_dir, ignore_errors=True)
|
||||
|
||||
@run_in_subprocess
|
||||
def test_log_file_size_invalid(self):
|
||||
with pytest.raises(TypeError):
|
||||
zvec.init(log_type=LogType.FILE, log_file_size="df")
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
zvec.init(log_type=LogType.FILE, log_file_size=0)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
zvec.init(log_type=LogType.FILE, log_file_size=-1)
|
||||
|
||||
@run_in_subprocess
|
||||
def test_log_overdue_days_invalid(self):
|
||||
with pytest.raises(TypeError):
|
||||
zvec.init(log_type=LogType.FILE, log_overdue_days="df")
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
zvec.init(log_type=LogType.FILE, log_overdue_days=0)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
zvec.init(log_type=LogType.FILE, log_overdue_days=-1)
|
||||
Reference in New Issue
Block a user