Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -232,18 +232,20 @@ allprojects {
}

afterEvaluate {
project.dependencyLicenses.enabled = false
project.thirdPartyAudit.enabled = false
project.loggerUsageCheck.enabled = false
project.forbiddenApis.ignoreFailures = true
project.forbiddenPatterns {
setEnabled(false)
if (project.name != 'e2e') {
project.dependencyLicenses.enabled = false
project.thirdPartyAudit.enabled = false
project.loggerUsageCheck.enabled = false
project.forbiddenApis.ignoreFailures = true
project.forbiddenPatterns {
setEnabled(false)
}
project.testingConventions.enabled = false
project.validateNebulaPom.enabled = false
project.forbiddenApis.ignoreFailures = true
}
project.testingConventions.enabled = false
project.validateNebulaPom.enabled = false
project.licenseFile = rootProject.file('LICENSE.txt')
project.noticeFile = rootProject.file('NOTICE.txt')
project.forbiddenApis.ignoreFailures = true
}
}

Expand Down
66 changes: 66 additions & 0 deletions e2e/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# OpenSearch k-NN E2E Tests

Python-based end-to-end tests for the OpenSearch k-NN plugin against external clusters.

## Setup

1. Install dependencies:
```bash
pip install -r requirements.txt
```

## Running Tests

### Using Gradle (Recommended):
```bash
# Run all tests against localhost:9200
./gradlew :e2e:runTests

# Run against custom cluster
./gradlew :e2e:runTests -Dopensearch.host=my-cluster.com -Dopensearch.port=9200

# Run against multi-node cluster
./gradlew :e2e:runTests -Dopensearch.nodes=3

# Run specific test pattern
./gradlew :e2e:runTests -Dtest.pattern="test_knn_basic"

# Run with custom credentials
./gradlew :e2e:runTests -Dopensearch.username=myuser -Dopensearch.password=mypass
```

### Using Python directly:
```bash
# All tests
python run_tests.py

# Specific test pattern
python run_tests.py -k "test_knn_basic"

# With pytest directly
pytest -v
```

## Test Structure

- `conftest.py` - Pytest fixtures and configuration
- `tests/test_knn_basic.py` - Basic k-NN functionality tests

## Configuration

### Gradle Task Parameters
- `-Dopensearch.host` - Cluster host (default: localhost)
- `-Dopensearch.port` - Cluster port (default: 9200)
- `-Dopensearch.username` - Username (default: admin)
- `-Dopensearch.password` - Password (default: admin)
- `-Dopensearch.ssl` - Use SSL (default: true)
- `-Dopensearch.nodes` - Number of nodes (default: 1)
- `-Dtest.pattern` - Test pattern to match

### Environment Variables
- `OPENSEARCH_HOST` - Cluster host (default: localhost)
- `OPENSEARCH_PORT` - Cluster port (default: 9200)
- `OPENSEARCH_USERNAME` - Username (default: admin)
- `OPENSEARCH_PASSWORD` - Password (default: admin)
- `OPENSEARCH_USE_SSL` - Use SSL (default: true)
- `OPENSEARCH_NODES` - Number of nodes (default: 1)
51 changes: 51 additions & 0 deletions e2e/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import org.apache.tools.ant.taskdefs.condition.Os

plugins {
id 'opensearch.build'
}


task runTests(type: Exec) {
description 'Run Python e2e tests against external OpenSearch cluster'
group 'verification'

def host = System.getProperty('opensearch.host', 'localhost')
def port = System.getProperty('opensearch.port', '9200')
def username = System.getProperty('opensearch.username', '')
def password = System.getProperty('opensearch.password', '')
def useSSL = System.getProperty('opensearch.ssl', 'false')
def numNodes = System.getProperty('opensearch.nodes', '1')
def testPattern = System.getProperty('test.pattern', '')

environment 'OPENSEARCH_HOST', host
environment 'OPENSEARCH_PORT', port
environment 'OPENSEARCH_USERNAME', username
environment 'OPENSEARCH_PASSWORD', password
environment 'OPENSEARCH_USE_SSL', useSSL
environment 'OPENSEARCH_NODES', numNodes

def pythonCmd = Os.isFamily(Os.FAMILY_WINDOWS) ? 'python' : 'python3'
def args = [pythonCmd, '-m', 'pytest', '-v']

if (testPattern) {
args.addAll(['-k', testPattern])
}

commandLine args

doFirst {
println "Running e2e tests against cluster at ${host}:${port} with ${numNodes} nodes"
if (testPattern) {
println "Test pattern: ${testPattern}"
}
}

doLast {
println "E2E tests completed"
}
}
89 changes: 89 additions & 0 deletions e2e/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Copyright OpenSearch Contributors
# SPDX-License-Identifier: Apache-2.0

import pytest
import os
from opensearchpy import OpenSearch
import uuid
from packaging import version
from version_utils import normalize_version
from faker import Faker

# Global variable to store cluster version
cluster_version = '1.0'

@pytest.fixture(scope="session")
def opensearch_client():
"""Create OpenSearch client for external cluster"""
client = create_client()
info = client.info()
num_nodes = int(os.getenv('OPENSEARCH_NODES', '1'))
print(f"Connected to OpenSearch {info['version']['number']} ({num_nodes} nodes)")
yield client
client.close()

@pytest.fixture
def faker():
"""Seeded faker for reproducible test data"""
fake = Faker()
fake.seed_instance(42)
return fake

@pytest.fixture
def test_index():
"""Generate unique test index name"""
return f"test_knn_{uuid.uuid4().hex[:8]}"


@pytest.fixture
def index_manager(opensearch_client):
"""Manages index cleanup after tests"""
indices_to_delete = []

def register_index(index_name):
indices_to_delete.append(index_name)
return index_name

yield register_index

# Cleanup all registered indices
for index in indices_to_delete:
opensearch_client.indices.delete(index=index, ignore=404)


def pytest_collection_modifyitems(config, items):
"""Filter tests based on cluster version"""
# Get cluster version during collection
try:
client = create_client()
info = client.info()
detected_version = normalize_version(info['version']['number'])
client.close()
print(f"Detected cluster version: {detected_version}")
except:
detected_version = '1.0'
print("Could not detect cluster version, using default 1.0")

for item in items:
min_ver = getattr(item.function, '_min_version', '1.0')
max_ver = getattr(item.function, '_max_version', '3.1')

if not (version.parse(min_ver) <= version.parse(detected_version) <= version.parse(max_ver)):
item.add_marker(pytest.mark.skip(f"Version {detected_version} not in range {min_ver}-{max_ver}"))

def create_client():
"""Create OpenSearch client for version detection"""
host = os.getenv('OPENSEARCH_HOST', 'localhost')
port = int(os.getenv('OPENSEARCH_PORT', '9200'))
username = os.getenv('OPENSEARCH_USERNAME', '')
password = os.getenv('OPENSEARCH_PASSWORD', '')
use_ssl = os.getenv('OPENSEARCH_USE_SSL', 'false').lower() == 'true'

return OpenSearch(
hosts=[{'host': host, 'port': port}],
http_auth=(username, password),
use_ssl=use_ssl,
verify_certs=False,
ssl_show_warn=False
)

9 changes: 9 additions & 0 deletions e2e/pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[tool:pytest]
testpaths = .
python_files = test_*.py
python_classes = Test*
python_functions = test_*
addopts = -v --tb=short
markers =
slow: marks tests as slow
integration: marks tests as integration tests
8 changes: 8 additions & 0 deletions e2e/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
opensearch-py>=2.0.0
pytest>=7.0.0
pytest-asyncio>=0.21.0
requests>=2.28.0
numpy>=1.21.0
pyyaml>=6.0
packaging>=21.0
faker>=37.4.0
42 changes: 42 additions & 0 deletions e2e/run_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#!/usr/bin/env python3

# Copyright OpenSearch Contributors
# SPDX-License-Identifier: Apache-2.0

"""
E2E Test Runner for OpenSearch k-NN Plugin
"""
import os
import sys
import subprocess
import argparse

def run_tests(test_pattern=None, verbose=False):
"""Run e2e tests against external OpenSearch cluster"""
cmd = ["python", "-m", "pytest"]

if verbose:
cmd.append("-v")

if test_pattern:
cmd.extend(["-k", test_pattern])

# Set environment
env = os.environ.copy()

print(f"Running command: {' '.join(cmd)}")
result = subprocess.run(cmd, env=env)
return result.returncode

def main():
parser = argparse.ArgumentParser(description="Run k-NN e2e tests")
parser.add_argument("-k", "--pattern", help="Test pattern to match")
parser.add_argument("-v", "--verbose", action="store_true", help="Verbose output")

args = parser.parse_args()

exit_code = run_tests(args.pattern, args.verbose)
sys.exit(exit_code)

if __name__ == "__main__":
main()
Loading
Loading