Merge commit 'd803bfe2b1fe7f5e219e50ac20d6801a0a58ac75' as 'vendor/ruvector'
This commit is contained in:
560
vendor/ruvector/crates/ruvector-postgres/docker/run-integration-tests.sh
vendored
Executable file
560
vendor/ruvector/crates/ruvector-postgres/docker/run-integration-tests.sh
vendored
Executable file
@@ -0,0 +1,560 @@
|
||||
#!/usr/bin/env bash
|
||||
# RuVector-Postgres Integration Test Runner
|
||||
# Builds Docker environment, runs comprehensive integration tests, and reports results
|
||||
|
||||
set -e # Exit on error
|
||||
set -u # Exit on undefined variable
|
||||
set -o pipefail # Exit on pipe failure
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Configuration
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)"
|
||||
COMPOSE_FILE="${SCRIPT_DIR}/docker-compose.integration.yml"
|
||||
TEST_RESULTS_DIR="${PROJECT_ROOT}/test-results/integration"
|
||||
POSTGRES_CONTAINER="ruvector-postgres-integration"
|
||||
TEST_RUNNER_CONTAINER="ruvector-integration-runner"
|
||||
|
||||
# Default settings
|
||||
PG_VERSION="${PG_VERSION:-17}"
|
||||
RUST_LOG="${RUST_LOG:-info}"
|
||||
TEST_TIMEOUT="${TEST_TIMEOUT:-600}"
|
||||
KEEP_RUNNING="${KEEP_RUNNING:-false}"
|
||||
|
||||
# Test categories
|
||||
declare -a TEST_CATEGORIES=(
|
||||
"pgvector_compat"
|
||||
"integrity_tests"
|
||||
"hybrid_search_tests"
|
||||
"tenancy_tests"
|
||||
"healing_tests"
|
||||
"perf_tests"
|
||||
)
|
||||
|
||||
# Functions
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
log_section() {
|
||||
echo -e "\n${CYAN}=== $1 ===${NC}\n"
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
if [ "${KEEP_RUNNING}" != "true" ]; then
|
||||
log_info "Cleaning up Docker containers..."
|
||||
docker-compose -f "${COMPOSE_FILE}" down -v 2>/dev/null || true
|
||||
docker rm -f "${POSTGRES_CONTAINER}" 2>/dev/null || true
|
||||
docker rm -f "${TEST_RUNNER_CONTAINER}" 2>/dev/null || true
|
||||
else
|
||||
log_info "Keeping containers running for debugging"
|
||||
fi
|
||||
}
|
||||
|
||||
wait_for_postgres() {
|
||||
log_info "Waiting for PostgreSQL to be ready..."
|
||||
local max_attempts=60
|
||||
local attempt=1
|
||||
|
||||
while [ ${attempt} -le ${max_attempts} ]; do
|
||||
if docker exec "${POSTGRES_CONTAINER}" pg_isready -U ruvector -d ruvector_test &>/dev/null; then
|
||||
log_success "PostgreSQL is ready!"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo -n "."
|
||||
sleep 1
|
||||
attempt=$((attempt + 1))
|
||||
done
|
||||
|
||||
log_error "PostgreSQL failed to start after ${max_attempts} seconds"
|
||||
docker logs "${POSTGRES_CONTAINER}" 2>&1 | tail -50
|
||||
return 1
|
||||
}
|
||||
|
||||
verify_extension() {
|
||||
log_info "Verifying RuVector extension..."
|
||||
|
||||
docker exec "${POSTGRES_CONTAINER}" psql -U ruvector -d ruvector_test -c "
|
||||
SELECT ruvector_version();
|
||||
SELECT ruvector_simd_info();
|
||||
" || {
|
||||
log_error "Failed to verify RuVector extension"
|
||||
return 1
|
||||
}
|
||||
|
||||
log_success "RuVector extension verified"
|
||||
}
|
||||
|
||||
build_extension() {
|
||||
log_section "Building RuVector Extension"
|
||||
|
||||
cd "${PROJECT_ROOT}"
|
||||
|
||||
DOCKER_BUILDKIT=1 docker build \
|
||||
-f crates/ruvector-postgres/docker/Dockerfile \
|
||||
-t "ruvector-postgres:pg${PG_VERSION}-test" \
|
||||
--build-arg PG_VERSION="${PG_VERSION}" \
|
||||
--progress=plain \
|
||||
. || {
|
||||
log_error "Failed to build extension"
|
||||
return 1
|
||||
}
|
||||
|
||||
log_success "Extension built successfully"
|
||||
}
|
||||
|
||||
start_postgres() {
|
||||
log_section "Starting PostgreSQL Container"
|
||||
|
||||
docker run -d \
|
||||
--name "${POSTGRES_CONTAINER}" \
|
||||
-e POSTGRES_USER=ruvector \
|
||||
-e POSTGRES_PASSWORD=ruvector \
|
||||
-e POSTGRES_DB=ruvector_test \
|
||||
-p 5433:5432 \
|
||||
--health-cmd="pg_isready -U ruvector -d ruvector_test" \
|
||||
--health-interval=5s \
|
||||
--health-timeout=5s \
|
||||
--health-retries=10 \
|
||||
"ruvector-postgres:pg${PG_VERSION}-test"
|
||||
|
||||
wait_for_postgres
|
||||
verify_extension
|
||||
}
|
||||
|
||||
setup_test_schema() {
|
||||
log_info "Setting up test schema..."
|
||||
|
||||
docker exec "${POSTGRES_CONTAINER}" psql -U ruvector -d ruvector_test << 'EOF'
|
||||
-- Create test schemas for each category
|
||||
CREATE SCHEMA IF NOT EXISTS test_pgvector;
|
||||
CREATE SCHEMA IF NOT EXISTS test_integrity;
|
||||
CREATE SCHEMA IF NOT EXISTS test_hybrid;
|
||||
CREATE SCHEMA IF NOT EXISTS test_tenancy;
|
||||
CREATE SCHEMA IF NOT EXISTS test_healing;
|
||||
CREATE SCHEMA IF NOT EXISTS test_perf;
|
||||
|
||||
-- Grant permissions
|
||||
GRANT ALL ON ALL SCHEMAS IN DATABASE ruvector_test TO ruvector;
|
||||
|
||||
-- Create test tables
|
||||
CREATE TABLE IF NOT EXISTS test_pgvector.vectors (
|
||||
id SERIAL PRIMARY KEY,
|
||||
embedding vector(128),
|
||||
metadata JSONB,
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS test_perf.benchmark_vectors (
|
||||
id SERIAL PRIMARY KEY,
|
||||
embedding vector(128),
|
||||
metadata JSONB,
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Create indexes
|
||||
CREATE INDEX IF NOT EXISTS test_pgvector_hnsw ON test_pgvector.vectors
|
||||
USING hnsw (embedding vector_l2_ops) WITH (m = 16, ef_construction = 64);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS test_perf_hnsw ON test_perf.benchmark_vectors
|
||||
USING hnsw (embedding vector_l2_ops) WITH (m = 16, ef_construction = 64);
|
||||
|
||||
-- Insert test data
|
||||
INSERT INTO test_pgvector.vectors (embedding, metadata)
|
||||
SELECT
|
||||
(SELECT array_agg(random()::real) FROM generate_series(1, 128))::vector,
|
||||
jsonb_build_object('idx', i)
|
||||
FROM generate_series(1, 1000) i;
|
||||
|
||||
ANALYZE test_pgvector.vectors;
|
||||
|
||||
\echo 'Test schema setup complete'
|
||||
EOF
|
||||
|
||||
log_success "Test schema created"
|
||||
}
|
||||
|
||||
run_unit_tests() {
|
||||
log_section "Running Unit Tests"
|
||||
|
||||
cd "${PROJECT_ROOT}/crates/ruvector-postgres"
|
||||
|
||||
# Run tests in release mode for performance
|
||||
cargo test \
|
||||
--release \
|
||||
--features "pg${PG_VERSION},graph-complete" \
|
||||
--lib \
|
||||
-- \
|
||||
--test-threads=4 \
|
||||
2>&1 | tee "${TEST_RESULTS_DIR}/unit_tests.log"
|
||||
|
||||
local exit_code=$?
|
||||
|
||||
if [ ${exit_code} -eq 0 ]; then
|
||||
log_success "Unit tests passed"
|
||||
else
|
||||
log_error "Unit tests failed"
|
||||
fi
|
||||
|
||||
return ${exit_code}
|
||||
}
|
||||
|
||||
run_integration_tests() {
|
||||
log_section "Running Integration Tests"
|
||||
|
||||
cd "${PROJECT_ROOT}/crates/ruvector-postgres"
|
||||
|
||||
export DATABASE_URL="postgresql://ruvector:ruvector@localhost:5433/ruvector_test"
|
||||
export RUST_LOG="${RUST_LOG}"
|
||||
export RUST_BACKTRACE=1
|
||||
|
||||
local failed_categories=()
|
||||
|
||||
for category in "${TEST_CATEGORIES[@]}"; do
|
||||
log_info "Running ${category} tests..."
|
||||
|
||||
cargo test \
|
||||
--release \
|
||||
--features "pg${PG_VERSION},graph-complete" \
|
||||
--test integration \
|
||||
"${category}" \
|
||||
-- \
|
||||
--test-threads=1 \
|
||||
2>&1 | tee "${TEST_RESULTS_DIR}/${category}.log"
|
||||
|
||||
if [ ${PIPESTATUS[0]} -ne 0 ]; then
|
||||
log_error "${category} tests failed"
|
||||
failed_categories+=("${category}")
|
||||
else
|
||||
log_success "${category} tests passed"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#failed_categories[@]} -gt 0 ]; then
|
||||
log_error "Failed test categories: ${failed_categories[*]}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_success "All integration tests passed"
|
||||
return 0
|
||||
}
|
||||
|
||||
run_sql_tests() {
|
||||
log_section "Running SQL Integration Tests"
|
||||
|
||||
local test_sql_dir="${SCRIPT_DIR}/test_sql"
|
||||
mkdir -p "${test_sql_dir}"
|
||||
|
||||
# Generate and run SQL tests
|
||||
cat > "${test_sql_dir}/pgvector_compat.sql" << 'EOF'
|
||||
-- pgvector compatibility tests
|
||||
\echo 'Testing pgvector compatibility...'
|
||||
|
||||
-- Test vector type
|
||||
SELECT '[1,2,3]'::vector AS test_vector;
|
||||
|
||||
-- Test operators
|
||||
SELECT '[1,2,3]'::vector <-> '[4,5,6]'::vector AS l2_distance;
|
||||
SELECT '[1,2,3]'::vector <=> '[4,5,6]'::vector AS cosine_distance;
|
||||
SELECT '[1,2,3]'::vector <#> '[4,5,6]'::vector AS inner_product;
|
||||
|
||||
-- Test nearest neighbor search
|
||||
SELECT id, embedding <-> '[0.5, 0.5, 0.5]'::vector(3) AS distance
|
||||
FROM (VALUES (1, '[1,2,3]'::vector), (2, '[2,3,4]'::vector)) AS t(id, embedding)
|
||||
ORDER BY embedding <-> '[0.5, 0.5, 0.5]'::vector(3)
|
||||
LIMIT 2;
|
||||
|
||||
\echo 'pgvector compatibility tests passed!'
|
||||
EOF
|
||||
|
||||
docker exec "${POSTGRES_CONTAINER}" psql -U ruvector -d ruvector_test \
|
||||
-f /dev/stdin < "${test_sql_dir}/pgvector_compat.sql" \
|
||||
2>&1 | tee "${TEST_RESULTS_DIR}/sql_tests.log"
|
||||
|
||||
log_success "SQL integration tests completed"
|
||||
}
|
||||
|
||||
run_performance_benchmark() {
|
||||
log_section "Running Performance Benchmark"
|
||||
|
||||
docker exec "${POSTGRES_CONTAINER}" psql -U ruvector -d ruvector_test << 'EOF'
|
||||
\timing on
|
||||
|
||||
-- Insert benchmark
|
||||
\echo 'Insert benchmark (1000 vectors)...'
|
||||
INSERT INTO test_perf.benchmark_vectors (embedding, metadata)
|
||||
SELECT
|
||||
(SELECT array_agg(random()::real) FROM generate_series(1, 128))::vector,
|
||||
jsonb_build_object('idx', i)
|
||||
FROM generate_series(1, 1000) i;
|
||||
|
||||
-- Query benchmark
|
||||
\echo 'Query benchmark (100 queries)...'
|
||||
DO $$
|
||||
DECLARE
|
||||
query_vec vector;
|
||||
start_time timestamp;
|
||||
total_time interval := '0'::interval;
|
||||
i integer;
|
||||
BEGIN
|
||||
FOR i IN 1..100 LOOP
|
||||
query_vec := (SELECT array_agg(random()::real) FROM generate_series(1, 128))::vector;
|
||||
start_time := clock_timestamp();
|
||||
|
||||
PERFORM id FROM test_perf.benchmark_vectors
|
||||
ORDER BY embedding <-> query_vec
|
||||
LIMIT 10;
|
||||
|
||||
total_time := total_time + (clock_timestamp() - start_time);
|
||||
END LOOP;
|
||||
|
||||
RAISE NOTICE 'Total time for 100 queries: %', total_time;
|
||||
RAISE NOTICE 'Average query time: %', total_time / 100;
|
||||
END;
|
||||
$$;
|
||||
|
||||
\echo 'Performance benchmark complete!'
|
||||
EOF
|
||||
|
||||
log_success "Performance benchmark completed"
|
||||
}
|
||||
|
||||
generate_report() {
|
||||
log_section "Generating Test Report"
|
||||
|
||||
local report_file="${TEST_RESULTS_DIR}/report.md"
|
||||
|
||||
cat > "${report_file}" << EOF
|
||||
# RuVector Postgres Integration Test Report
|
||||
|
||||
Generated: $(date -Iseconds)
|
||||
PostgreSQL Version: ${PG_VERSION}
|
||||
|
||||
## Test Results Summary
|
||||
|
||||
| Category | Status |
|
||||
|----------|--------|
|
||||
EOF
|
||||
|
||||
for category in "${TEST_CATEGORIES[@]}"; do
|
||||
local status="PASS"
|
||||
if grep -q "FAILED" "${TEST_RESULTS_DIR}/${category}.log" 2>/dev/null; then
|
||||
status="FAIL"
|
||||
fi
|
||||
echo "| ${category} | ${status} |" >> "${report_file}"
|
||||
done
|
||||
|
||||
cat >> "${report_file}" << EOF
|
||||
|
||||
## Test Categories
|
||||
|
||||
### pgvector Compatibility
|
||||
- Vector type creation and operators
|
||||
- HNSW and IVFFlat index creation
|
||||
- Basic CRUD operations
|
||||
|
||||
### Integrity System
|
||||
- Contracted graph construction
|
||||
- Mincut computation
|
||||
- State transitions
|
||||
|
||||
### Hybrid Search
|
||||
- BM25 scoring accuracy
|
||||
- RRF fusion
|
||||
- Linear fusion
|
||||
|
||||
### Multi-Tenancy
|
||||
- Schema isolation
|
||||
- RLS policies
|
||||
- Quota enforcement
|
||||
|
||||
### Self-Healing
|
||||
- Problem detection
|
||||
- Remediation strategies
|
||||
- Recovery from failures
|
||||
|
||||
### Performance
|
||||
- Insert throughput
|
||||
- Query latency (p50, p95, p99)
|
||||
- SIMD acceleration
|
||||
- Concurrent scaling
|
||||
|
||||
## Logs
|
||||
|
||||
Test logs are available in: ${TEST_RESULTS_DIR}/
|
||||
|
||||
## Environment
|
||||
|
||||
- Docker: $(docker --version)
|
||||
- Rust: $(rustc --version)
|
||||
- PostgreSQL: ${PG_VERSION}
|
||||
EOF
|
||||
|
||||
log_success "Report generated: ${report_file}"
|
||||
}
|
||||
|
||||
show_usage() {
|
||||
cat << EOF
|
||||
RuVector-Postgres Integration Test Runner
|
||||
|
||||
Usage: $0 [OPTIONS]
|
||||
|
||||
Options:
|
||||
-b, --build-only Build Docker image only
|
||||
-t, --tests-only Run tests only (skip build)
|
||||
-c, --category CAT Run specific test category
|
||||
-s, --sql-only Run SQL tests only
|
||||
-p, --perf Run performance benchmarks
|
||||
-k, --keep-running Keep containers after tests
|
||||
--pg-version VER PostgreSQL version (default: 17)
|
||||
-h, --help Show this help
|
||||
|
||||
Test Categories:
|
||||
pgvector_compat pgvector SQL compatibility
|
||||
integrity_tests Integrity system tests
|
||||
hybrid_search_tests Hybrid search tests
|
||||
tenancy_tests Multi-tenancy tests
|
||||
healing_tests Self-healing tests
|
||||
perf_tests Performance tests
|
||||
|
||||
Examples:
|
||||
# Run all tests
|
||||
$0
|
||||
|
||||
# Run specific category
|
||||
$0 -c pgvector_compat
|
||||
|
||||
# Run performance benchmark only
|
||||
$0 -p
|
||||
|
||||
# Keep containers for debugging
|
||||
$0 -k
|
||||
EOF
|
||||
}
|
||||
|
||||
main() {
|
||||
local build_only=false
|
||||
local tests_only=false
|
||||
local sql_only=false
|
||||
local perf_only=false
|
||||
local specific_category=""
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-b|--build-only)
|
||||
build_only=true
|
||||
shift
|
||||
;;
|
||||
-t|--tests-only)
|
||||
tests_only=true
|
||||
shift
|
||||
;;
|
||||
-c|--category)
|
||||
specific_category="$2"
|
||||
shift 2
|
||||
;;
|
||||
-s|--sql-only)
|
||||
sql_only=true
|
||||
shift
|
||||
;;
|
||||
-p|--perf)
|
||||
perf_only=true
|
||||
shift
|
||||
;;
|
||||
-k|--keep-running)
|
||||
KEEP_RUNNING=true
|
||||
shift
|
||||
;;
|
||||
--pg-version)
|
||||
PG_VERSION="$2"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
show_usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
log_error "Unknown option: $1"
|
||||
show_usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Setup trap for cleanup
|
||||
trap cleanup EXIT
|
||||
|
||||
# Create results directory
|
||||
mkdir -p "${TEST_RESULTS_DIR}"
|
||||
|
||||
log_section "RuVector Integration Test Suite"
|
||||
log_info "PostgreSQL Version: ${PG_VERSION}"
|
||||
log_info "Results Directory: ${TEST_RESULTS_DIR}"
|
||||
|
||||
# Build phase
|
||||
if [ "${tests_only}" != "true" ]; then
|
||||
build_extension
|
||||
fi
|
||||
|
||||
if [ "${build_only}" == "true" ]; then
|
||||
log_success "Build complete!"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Start PostgreSQL
|
||||
start_postgres
|
||||
setup_test_schema
|
||||
|
||||
# Run tests
|
||||
local test_result=0
|
||||
|
||||
if [ "${sql_only}" == "true" ]; then
|
||||
run_sql_tests || test_result=$?
|
||||
elif [ "${perf_only}" == "true" ]; then
|
||||
run_performance_benchmark || test_result=$?
|
||||
elif [ -n "${specific_category}" ]; then
|
||||
TEST_CATEGORIES=("${specific_category}")
|
||||
run_integration_tests || test_result=$?
|
||||
else
|
||||
# Run all tests
|
||||
run_unit_tests || test_result=$?
|
||||
run_integration_tests || test_result=$?
|
||||
run_sql_tests || test_result=$?
|
||||
run_performance_benchmark || test_result=$?
|
||||
fi
|
||||
|
||||
# Generate report
|
||||
generate_report
|
||||
|
||||
if [ ${test_result} -eq 0 ]; then
|
||||
log_success "All tests completed successfully!"
|
||||
else
|
||||
log_error "Some tests failed. Check logs in ${TEST_RESULTS_DIR}/"
|
||||
fi
|
||||
|
||||
exit ${test_result}
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user