blob: 5c6925c7b5cd933150b13d25a446db61a3fb1a12 [file]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
from unittest.mock import patch
import pytest
pytest.importorskip("ray")
from pypaimon.ray import batch_vector_search, vector_search
from pypaimon.tests import ray_vector_search_test as fixtures
from pypaimon.tests.ray_vector_search_test import VECTORS, add_rows, build_index
ray_cluster = fixtures.ray_cluster
table = fixtures.table
def make_query(table, batch):
queries = [[1., 1.], [-1., 2.], [1., 1.]]
return (table.search_vectors(queries) if batch else table.search(queries[0])).select(["id"]).limit(2)
@pytest.mark.parametrize("batch", [False, True])
@pytest.mark.parametrize("method", ["to_pandas", "to_list"])
@pytest.mark.parametrize("indexed", [False, True])
def test_ray_result_conversion(table, ray_cluster, batch, method, indexed):
add_rows(table, VECTORS)
if indexed:
build_index(table)
add_rows(table, [[1., 1.]], 6)
query = make_query(table, batch)
expected = query.to_list()
module = batch_vector_search if batch else vector_search
name = "_execute_batch_vector_search" if batch else "_execute_vector_search"
with patch.object(module, name, wraps=getattr(module, name)) as execute:
actual = getattr(query, method)(
execution="ray", concurrency=2, ray_remote_args={"num_cpus": 1})
execute.assert_called_once()
assert execute.call_args[1] == {"concurrency": 2, "ray_remote_args": {"num_cpus": 1}}
if method == "to_pandas":
actual = [frame.to_dict("records") for frame in actual] if batch else actual.to_dict("records")
assert actual == expected
@pytest.mark.parametrize("batch", [False, True])
@pytest.mark.parametrize("method", ["to_pandas", "to_list"])
def test_conversion_keeps_empty_results_and_default_local_execution(table, batch, method):
query = make_query(table, batch)
with patch.object(vector_search, "_execute_vector_search", side_effect=AssertionError("Ray dispatch")), \
patch.object(batch_vector_search, "_execute_batch_vector_search",
side_effect=AssertionError("Ray dispatch")):
actual = getattr(query, method)()
values = actual if batch else [actual]
assert len(values) == (3 if batch else 1)
assert all(len(value) == 0 for value in values)
if method == "to_pandas":
assert all(list(frame.columns) == ["id"] for frame in values)
@pytest.mark.parametrize("batch", [False, True])
@pytest.mark.parametrize("method", ["to_pandas", "to_list"])
def test_conversion_validates_options_and_propagates_failure(table, batch, method):
query = make_query(table, batch)
with pytest.raises(ValueError, match="execution must"):
getattr(query, method)(execution="invalid")
with pytest.raises(ValueError, match="require execution"):
getattr(query, method)(concurrency=2)
module = batch_vector_search if batch else vector_search
name = "_execute_batch_vector_search" if batch else "_execute_vector_search"
failure = RuntimeError("worker failed")
with patch.object(module, name, side_effect=failure), pytest.raises(RuntimeError) as exc:
getattr(query, method)(execution="ray")
assert exc.value is failure