--- /dev/null
+import logging
+import json
+import tempfile
+import random
+import socket
+import time
+import threading
+import subprocess
+import os
+import stat
+import string
+import pytest
+import boto3
+from botocore.config import Config
+
+from . import(
+ configfile,
+ get_config_host,
+ get_config_port,
+ get_access_key,
+ get_secret_key
+ )
+
+
+# configure logging for the tests module
+log = logging.getLogger(__name__)
+
+num_buckets = 0
+run_prefix=''.join(random.choice(string.ascii_lowercase) for _ in range(6))
+
+test_path = os.path.normpath(os.path.dirname(os.path.realpath(__file__))) + '/../'
+
+def bash(cmd, **kwargs):
+ log.debug('running command: %s', ' '.join(cmd))
+ kwargs['stdout'] = subprocess.PIPE
+ process = subprocess.Popen(cmd, **kwargs)
+ s = process.communicate()[0].decode('utf-8')
+ return (s, process.returncode)
+
+
+def admin(args, **kwargs):
+ """ radosgw-admin command """
+ cmd = [test_path + 'test-rgw-call.sh', 'call_rgw_admin', 'noname'] + args
+ return bash(cmd, **kwargs)
+
+
+def gen_bucket_name():
+ global num_buckets
+
+ num_buckets += 1
+ return run_prefix + '-' + str(num_buckets)
+
+
+def connection():
+ hostname = get_config_host()
+ port_no = get_config_port()
+ access_key = get_access_key()
+ secret_key = get_secret_key()
+ if port_no == 443 or port_no == 8443:
+ scheme = 'https://'
+ else:
+ scheme = 'http://'
+
+ client = boto3.client('s3vectors',
+ endpoint_url=scheme+hostname+':'+str(port_no),
+ aws_access_key_id=access_key,
+ aws_secret_access_key=secret_key,
+ config=Config(signature_version='s3'))
+
+ return client
+
+
+def another_user(tenant=None):
+ access_key = str(time.time())
+ secret_key = str(time.time())
+ uid = 'superman' + str(time.time())
+ if tenant:
+ _, result = admin(['user', 'create', '--uid', uid, '--tenant', tenant, '--access-key', access_key, '--secret-key', secret_key, '--display-name', '"Super Man"'])
+ else:
+ _, result = admin(['user', 'create', '--uid', uid, '--access-key', access_key, '--secret-key', secret_key, '--display-name', '"Super Man"'])
+
+ assert result == 0
+ hostname = get_config_host()
+ port_no = get_config_port()
+ if port_no == 443 or port_no == 8443:
+ scheme = 'https://'
+ else:
+ scheme = 'http://'
+
+ client = boto3.client('s3vectors',
+ endpoint_url=scheme+hostname+':'+str(port_no),
+ aws_access_key_id=access_key,
+ aws_secret_access_key=secret_key,
+ config=Config(signature_version='s3'))
+
+ return client
+
+
+#################
+# s3vectors tests
+#################
+
+@pytest.mark.vector_bucket_test
+def test_create_vector_bucket():
+ conn = connection()
+ bucket_name = gen_bucket_name()
+ result = conn.create_vector_bucket(vectorBucketName=bucket_name)
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ # cleanup
+ _ = conn.delete_vector_bucket(vectorBucketName=bucket_name)
+
+
+@pytest.mark.vector_bucket_test
+def test_get_vector_bucket():
+ conn = connection()
+ bucket_name = gen_bucket_name()
+ result = conn.create_vector_bucket(vectorBucketName=bucket_name)
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ result = conn.get_vector_bucket(vectorBucketName=bucket_name)
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ # cleanup
+ _ = conn.delete_vector_bucket(vectorBucketName=bucket_name)
+
+
+@pytest.mark.vector_bucket_test
+def test_delete_vector_bucket():
+ conn = connection()
+ bucket_name = gen_bucket_name()
+ result = conn.create_vector_bucket(vectorBucketName=bucket_name)
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ result = conn.get_vector_bucket(vectorBucketName=bucket_name)
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ result = conn.delete_vector_bucket(vectorBucketName=bucket_name)
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ # not implemented yet
+ #with pytest.raises(conn.exceptions.NoSuchVectorBucket):
+ # result = conn.get_vector_bucket(vectorBucketName=bucket_name)
+
+
+@pytest.mark.vector_bucket_test
+def test_list_vector_bucket():
+ conn = connection()
+ bucket_name1 = gen_bucket_name()
+ bucket_name2 = gen_bucket_name()
+ result = conn.create_vector_bucket(vectorBucketName=bucket_name1)
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ result = conn.create_vector_bucket(vectorBucketName=bucket_name2)
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ result = conn.list_vector_buckets()
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ # not implemented yet
+ #bucket_names = [b['Name'] for b in result['VectorBuckets']]
+ #assert bucket_name1 in bucket_names
+ #assert bucket_name2 in bucket_names
+ # cleanup
+ _ = conn.delete_vector_bucket(vectorBucketName=bucket_name1)
+ _ = conn.delete_vector_bucket(vectorBucketName=bucket_name2)
+
+
+@pytest.mark.index_test
+def test_create_index():
+ conn = connection()
+ bucket_name = gen_bucket_name()
+ result = conn.create_vector_bucket(vectorBucketName=bucket_name)
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ index_name = 'test-index'
+ result = conn.create_index(vectorBucketName=bucket_name, indexName=index_name, dataType='float32', dimension=128, distanceMetric='euclidean')
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ # cleanup
+ _ = conn.delete_vector_bucket(vectorBucketName=bucket_name)
+
+
+@pytest.mark.index_test
+def test_get_index():
+ conn = connection()
+ bucket_name = gen_bucket_name()
+ result = conn.create_vector_bucket(vectorBucketName=bucket_name)
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ index_name = 'test-index'
+ result = conn.create_index(vectorBucketName=bucket_name, indexName=index_name, dataType='float32', dimension=128, distanceMetric='euclidean')
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ result = conn.get_index(vectorBucketName=bucket_name, indexName=index_name)
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ # cleanup
+ _ = conn.delete_vector_bucket(vectorBucketName=bucket_name)
+
+
+@pytest.mark.index_test
+def test_delete_index():
+ conn = connection()
+ bucket_name = gen_bucket_name()
+ result = conn.create_vector_bucket(vectorBucketName=bucket_name)
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ index_name = 'test-index'
+ result = conn.create_index(vectorBucketName=bucket_name, indexName=index_name, dataType='float32', dimension=128, distanceMetric='euclidean')
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ result = conn.get_index(vectorBucketName=bucket_name, indexName=index_name)
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ result = conn.delete_index(vectorBucketName=bucket_name, indexName=index_name)
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ # not implemented yet
+ #with pytest.raises(conn.exceptions.NoSuchIndex):
+ # result = conn.get_index(vectorBucketName=bucket_name, indexName=index_name)
+ # cleanup
+ _ = conn.delete_vector_bucket(vectorBucketName=bucket_name)
+
+
+@pytest.mark.index_test
+def test_list_indexes():
+ conn = connection()
+ bucket_name = gen_bucket_name()
+ result = conn.create_vector_bucket(vectorBucketName=bucket_name)
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ index_name1 = 'test-index1'
+ index_name2 = 'test-index2'
+ result = conn.create_index(vectorBucketName=bucket_name, indexName=index_name1, dataType='float32', dimension=128, distanceMetric='cosine')
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ result = conn.create_index(vectorBucketName=bucket_name, indexName=index_name2, dataType='float32', dimension=128, distanceMetric='euclidean')
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ result = conn.list_indexes(vectorBucketName=bucket_name)
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ # not implemented yet
+ #index_names = [i['IndexName'] for i in result['Indexes']]
+ #assert index_name1 in index_names
+ #assert index_name2 in index_names
+ # cleanup
+ _ = conn.delete_vector_bucket(vectorBucketName=bucket_name)
+
+
+def generate_data(dimension):
+ return {'float32': [float(j) for j in range(dimension)]}
+
+
+def generate_vectors(num_vectors, dimension):
+ vectors = []
+ for i in range(num_vectors):
+ vectors.append({
+ 'key': 'vec-' + str(i),
+ 'data': generate_data(dimension)
+ })
+ return vectors
+
+
+@pytest.mark.vector_test
+def test_put_vectors():
+ conn = connection()
+ bucket_name = gen_bucket_name()
+ result = conn.create_vector_bucket(vectorBucketName=bucket_name)
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ index_name = 'test-index'
+ result = conn.create_index(vectorBucketName=bucket_name, indexName=index_name, dataType='float32', dimension=128, distanceMetric='euclidean')
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ vectors = generate_vectors(10, 128)
+ result = conn.put_vectors(vectorBucketName=bucket_name, indexName=index_name, vectors=vectors)
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ # cleanup
+ _ = conn.delete_vector_bucket(vectorBucketName=bucket_name)
+
+
+@pytest.mark.vector_test
+def test_get_vectors():
+ conn = connection()
+ bucket_name = gen_bucket_name()
+ result = conn.create_vector_bucket(vectorBucketName=bucket_name)
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ index_name = 'test-index'
+ result = conn.create_index(vectorBucketName=bucket_name, indexName=index_name, dataType='float32', dimension=128, distanceMetric='euclidean')
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ vectors = generate_vectors(10, 128)
+ result = conn.put_vectors(vectorBucketName=bucket_name, indexName=index_name, vectors=vectors)
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ vector_ids = ['vec-' + str(i) for i in range(10)]
+ result = conn.get_vectors(vectorBucketName=bucket_name, indexName=index_name, keys=vector_ids)
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ # not implemented yet
+ #assert len(result['Vectors']) == 10
+ # cleanup
+ _ = conn.delete_vector_bucket(vectorBucketName=bucket_name)
+
+
+@pytest.mark.vector_test
+def test_list_vectors():
+ conn = connection()
+ bucket_name = gen_bucket_name()
+ result = conn.create_vector_bucket(vectorBucketName=bucket_name)
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ index_name = 'test-index'
+ result = conn.create_index(vectorBucketName=bucket_name, indexName=index_name, dataType='float32', dimension=128, distanceMetric='euclidean')
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ vectors = generate_vectors(10, 128)
+ result = conn.put_vectors(vectorBucketName=bucket_name, indexName=index_name, vectors=vectors)
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ result = conn.list_vectors(vectorBucketName=bucket_name, indexName=index_name)
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ # not implemented yet
+ #assert len(result['Vectors']) == 10
+ # cleanup
+ _ = conn.delete_vector_bucket(vectorBucketName=bucket_name)
+
+
+@pytest.mark.vector_test
+def test_delete_vectors():
+ conn = connection()
+ bucket_name = gen_bucket_name()
+ result = conn.create_vector_bucket(vectorBucketName=bucket_name)
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ index_name = 'test-index'
+ result = conn.create_index(vectorBucketName=bucket_name, indexName=index_name, dataType='float32', dimension=128, distanceMetric='euclidean')
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ vectors = generate_vectors(10, 128)
+ result = conn.put_vectors(vectorBucketName=bucket_name, indexName=index_name, vectors=vectors)
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ vector_ids = ['vec-' + str(i) for i in range(10)]
+ result = conn.delete_vectors(vectorBucketName=bucket_name, indexName=index_name, keys=vector_ids)
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ result = conn.list_vectors(vectorBucketName=bucket_name, indexName=index_name)
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ # not implemented yet
+ #assert len(result['Vectors']) == 0
+ # cleanup
+ _ = conn.delete_vector_bucket(vectorBucketName=bucket_name)
+
+
+@pytest.mark.vector_test
+def test_query_vectors():
+ conn = connection()
+ bucket_name = gen_bucket_name()
+ result = conn.create_vector_bucket(vectorBucketName=bucket_name)
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ index_name = 'test-index'
+ result = conn.create_index(vectorBucketName=bucket_name, indexName=index_name, dataType='float32', dimension=128, distanceMetric='euclidean')
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ vectors = generate_vectors(10, 128)
+ result = conn.put_vectors(vectorBucketName=bucket_name, indexName=index_name, vectors=vectors)
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ query_vector = generate_data(128)
+ result = conn.query_vectors(vectorBucketName=bucket_name, indexName=index_name, queryVector=query_vector, topK=5)
+ assert result['ResponseMetadata']['HTTPStatusCode'] == 200
+ # not implemented yet
+ #assert len(result['Results']) == 5
+ # cleanup
+ _ = conn.delete_vector_bucket(vectorBucketName=bucket_name)
+