// 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. #include "zvec/c_api.h" #include #include #include #include #include #include // Platform-specific headers #ifdef _WIN32 #include #else #include #include #include #endif #include "utils.h" // ============================================================================= // Test helper macro definitions // ============================================================================= // Helper function to get field count (replaces GCC-specific statement // expression) static size_t get_field_count(zvec_collection_schema_t *schema) { const char **names = NULL; size_t count = 0; zvec_error_code_t err = zvec_collection_schema_get_all_field_names(schema, &names, &count); if (err == ZVEC_OK && names) { size_t i; for (i = 0; i < count; i++) { zvec_free((char *)names[i]); } zvec_free(names); } return count; } // Cross-platform helper function to clean up temporary directories static void cleanup_temp_directory(const char *dir) { zvec_test_delete_dir(dir); } static int test_count = 0; static int passed_count = 0; static int current_test_passed = 1; // Track if current test function passes #define TEST_START() \ do { \ printf("Running test: %s\n", __func__); \ test_count++; \ current_test_passed = 1; \ } while (0) #define TEST_ASSERT(condition) \ do { \ if (condition) { \ printf(" ✓ PASS\n"); \ } else { \ printf(" ✗ FAIL at line %d\n", __LINE__); \ current_test_passed = 0; \ } \ } while (0) #define TEST_END() \ do { \ if (current_test_passed) { \ passed_count++; \ } \ } while (0) // ============================================================================= // Helper functions tests // ============================================================================= void test_version_functions(void) { TEST_START(); // Test version retrieval functions const char *version = zvec_get_version(); TEST_ASSERT(version != NULL); printf(" Version string: %s\n", version); // Test version component retrieval int major = zvec_get_version_major(); int minor = zvec_get_version_minor(); int patch = zvec_get_version_patch(); printf(" Version components: %d.%d.%d\n", major, minor, patch); TEST_ASSERT(major >= 0); TEST_ASSERT(minor >= 0); TEST_ASSERT(patch >= 0); // Test version compatibility check with current version (should pass) TEST_ASSERT(zvec_check_version(major, minor, patch)); // Test with older version (should pass - current is newer) if (minor > 0) { TEST_ASSERT(zvec_check_version(major, minor - 1, patch)); } if (major > 0) { TEST_ASSERT(zvec_check_version(major - 1, minor, patch)); } // Test with much newer version (should fail - current is older) bool not_compatible = zvec_check_version(99, 99, 99); TEST_ASSERT(not_compatible == false); // Test with invalid negative versions (should fail and set error) TEST_ASSERT(zvec_check_version(-1, 0, 0) == false); TEST_ASSERT(zvec_check_version(0, -1, 0) == false); TEST_ASSERT(zvec_check_version(0, 0, -1) == false); TEST_END(); } void test_error_handling_functions(void) { TEST_START(); char *error_msg = NULL; zvec_error_code_t err = zvec_get_last_error(&error_msg); TEST_ASSERT(err == ZVEC_OK); if (error_msg) { zvec_free(error_msg); } // Test error clearing zvec_clear_error(); // Test error details retrieval zvec_error_details_t error_details = {0}; err = zvec_get_last_error_details(&error_details); TEST_ASSERT(err == ZVEC_OK); TEST_END(); } void test_zvec_config() { TEST_START(); // Test 1: Console log config creation and destruction zvec_log_config_t *console_config = zvec_config_log_create_console(ZVEC_LOG_LEVEL_INFO); TEST_ASSERT(console_config != NULL); if (console_config) { TEST_ASSERT(zvec_config_log_get_level(console_config) == ZVEC_LOG_LEVEL_INFO); zvec_config_log_destroy(console_config); } // Test 2: File log config creation and destruction zvec_log_config_t *file_config = zvec_config_log_create_file( ZVEC_LOG_LEVEL_WARN, "./logs", "test_log", 100, 7); TEST_ASSERT(file_config != NULL); if (file_config) { TEST_ASSERT(zvec_config_log_get_level(file_config) == ZVEC_LOG_LEVEL_WARN); TEST_ASSERT(strcmp(zvec_config_log_get_dir(file_config), "./logs") == 0); TEST_ASSERT(strcmp(zvec_config_log_get_basename(file_config), "test_log") == 0); TEST_ASSERT(zvec_config_log_get_file_size(file_config) == 100); TEST_ASSERT(zvec_config_log_get_overdue_days(file_config) == 7); zvec_config_log_destroy(file_config); } // Test 3: File log config edge cases zvec_log_config_t *empty_file_config = zvec_config_log_create_file(ZVEC_LOG_LEVEL_INFO, "", "", 0, 0); TEST_ASSERT(empty_file_config != NULL); if (empty_file_config) { TEST_ASSERT(zvec_config_log_get_level(empty_file_config) == ZVEC_LOG_LEVEL_INFO); TEST_ASSERT(strcmp(zvec_config_log_get_dir(empty_file_config), "") == 0); TEST_ASSERT(strcmp(zvec_config_log_get_basename(empty_file_config), "") == 0); TEST_ASSERT(zvec_config_log_get_file_size(empty_file_config) == 0); TEST_ASSERT(zvec_config_log_get_overdue_days(empty_file_config) == 0); zvec_config_log_destroy(empty_file_config); } // Test 4: Log config creation with console type zvec_log_config_t *temp_console = zvec_config_log_create_console(ZVEC_LOG_LEVEL_ERROR); TEST_ASSERT(temp_console != NULL); if (temp_console) { zvec_config_log_destroy(temp_console); } // Test 5: Log config creation with file type zvec_log_config_t *temp_file = zvec_config_log_create_file( ZVEC_LOG_LEVEL_DEBUG, "./logs", "app", 50, 30); TEST_ASSERT(temp_file != NULL); TEST_ASSERT(zvec_config_log_get_level(temp_file) == ZVEC_LOG_LEVEL_DEBUG); TEST_ASSERT(strcmp(zvec_config_log_get_dir(temp_file), "./logs") == 0); TEST_ASSERT(strcmp(zvec_config_log_get_basename(temp_file), "app") == 0); TEST_ASSERT(zvec_config_log_get_file_size(temp_file) == 50); TEST_ASSERT(zvec_config_log_get_overdue_days(temp_file) == 30); zvec_config_log_destroy(temp_file); // Test 6: Config data creation and basic operations zvec_config_data_t *config_data = zvec_config_data_create(); TEST_ASSERT(config_data != NULL); if (config_data) { // Test initial values TEST_ASSERT(zvec_config_data_get_log_type(config_data) == ZVEC_LOG_TYPE_CONSOLE); // Test memory limit setting zvec_error_code_t err = zvec_config_data_set_memory_limit(config_data, 1024 * 1024 * 1024); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(zvec_config_data_get_memory_limit(config_data) == 1024 * 1024 * 1024); // Test thread count settings err = zvec_config_data_set_query_thread_count(config_data, 8); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(zvec_config_data_get_query_thread_count(config_data) == 8); err = zvec_config_data_set_optimize_thread_count(config_data, 4); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(zvec_config_data_get_optimize_thread_count(config_data) == 4); // Test log config replacement TEST_ASSERT(zvec_config_data_get_log_type(config_data) == ZVEC_LOG_TYPE_CONSOLE); zvec_log_config_t *new_file = zvec_config_log_create_file( ZVEC_LOG_LEVEL_DEBUG, "./logs", "app", 50, 30); TEST_ASSERT(new_file != NULL); zvec_config_data_set_log_config(config_data, new_file); TEST_ASSERT(zvec_config_data_get_log_type(config_data) == ZVEC_LOG_TYPE_FILE); zvec_config_data_destroy(config_data); } // Test 7: Edge cases and error conditions // Test NULL pointer handling zvec_error_code_t err = zvec_config_data_set_memory_limit(NULL, 1024); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); err = zvec_config_data_set_log_config(NULL, NULL); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); err = zvec_config_data_set_query_thread_count(NULL, 1); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); err = zvec_config_data_set_optimize_thread_count(NULL, 1); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); // Test boundary values zvec_config_data_t *boundary_config = zvec_config_data_create(); if (boundary_config) { // Test zero values err = zvec_config_data_set_memory_limit(boundary_config, 0); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(zvec_config_data_get_memory_limit(boundary_config) == 0); // Test maximum values err = zvec_config_data_set_memory_limit(boundary_config, UINT64_MAX); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(zvec_config_data_get_memory_limit(boundary_config) == UINT64_MAX); // Test zero thread counts err = zvec_config_data_set_query_thread_count(boundary_config, 0); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(zvec_config_data_get_query_thread_count(boundary_config) == 0); err = zvec_config_data_set_optimize_thread_count(boundary_config, 0); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(zvec_config_data_get_optimize_thread_count(boundary_config) == 0); zvec_config_data_destroy(boundary_config); } // Test 8: Memory leak prevention - double destroy safety zvec_config_data_t *double_destroy_test = zvec_config_data_create(); if (double_destroy_test) { zvec_config_data_destroy(double_destroy_test); } TEST_END(); } void test_zvec_initialize() { TEST_START(); zvec_config_data_t *config = zvec_config_data_create(); TEST_ASSERT(config != NULL); if (config) { TEST_ASSERT(zvec_config_data_get_log_type(config) == ZVEC_LOG_TYPE_CONSOLE); } zvec_error_code_t err = zvec_initialize(config); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(zvec_is_initialized()); TEST_END(); } // ============================================================================= // Schema-related tests // ============================================================================= void test_schema_basic_operations(void) { TEST_START(); // Test 1: Basic Schema creation and destruction zvec_collection_schema_t *schema = zvec_collection_schema_create("demo"); TEST_ASSERT(schema != NULL); TEST_ASSERT(zvec_collection_schema_get_name(schema) != NULL); TEST_ASSERT(strcmp(zvec_collection_schema_get_name(schema), "demo") == 0); TEST_ASSERT(get_field_count(schema) == 0); TEST_ASSERT(zvec_collection_schema_get_max_doc_count_per_segment(schema) > 0); // Test 2: Schema field count operations size_t initial_count = get_field_count(schema); TEST_ASSERT(initial_count == 0); // Test 3: Adding fields to schema zvec_field_schema_t *id_field = zvec_field_schema_create("id", ZVEC_DATA_TYPE_INT64, false, 0); zvec_error_code_t err = zvec_collection_schema_add_field(schema, id_field); TEST_ASSERT(err == ZVEC_OK); size_t count_after_add = get_field_count(schema); TEST_ASSERT(count_after_add == 1); // Test 4: Finding fields in schema const zvec_field_schema_t *found_field = zvec_collection_schema_get_field(schema, "id"); TEST_ASSERT(found_field != NULL); TEST_ASSERT(strcmp(zvec_field_schema_get_name(found_field), "id") == 0); TEST_ASSERT(zvec_field_schema_get_data_type(found_field) == ZVEC_DATA_TYPE_INT64); // Test 5: Getting field by index (using get_all_field_names) const char **field_names = NULL; size_t field_count = 0; err = zvec_collection_schema_get_all_field_names(schema, &field_names, &field_count); TEST_ASSERT(err == ZVEC_OK && field_count > 0); const zvec_field_schema_t *indexed_field = NULL; if (field_count > 0) { indexed_field = zvec_collection_schema_get_field(schema, field_names[0]); } TEST_ASSERT(indexed_field != NULL); TEST_ASSERT(strcmp(zvec_field_schema_get_name(indexed_field), "id") == 0); // Clean up field names for (size_t i = 0; i < field_count; i++) zvec_free((char *)field_names[i]); zvec_free(field_names); // Test 6: Adding multiple fields (use individual add_field calls) zvec_field_schema_t *name_field = zvec_field_schema_create("name", ZVEC_DATA_TYPE_STRING, false, 0); zvec_field_schema_t *age_field = zvec_field_schema_create("age", ZVEC_DATA_TYPE_INT32, true, 0); err = zvec_collection_schema_add_field(schema, name_field); TEST_ASSERT(err == ZVEC_OK); err = zvec_collection_schema_add_field(schema, age_field); TEST_ASSERT(err == ZVEC_OK); // Add a vector field (required for validation) zvec_field_schema_t *vec_field = zvec_field_schema_create( "embedding", ZVEC_DATA_TYPE_VECTOR_FP32, false, 128); err = zvec_collection_schema_add_field(schema, vec_field); TEST_ASSERT(err == ZVEC_OK); size_t count_after_multi_add = get_field_count(schema); TEST_ASSERT(count_after_multi_add == 4); // id, name, age, embedding // Test 7: Finding newly added fields const zvec_field_schema_t *name_found = zvec_collection_schema_get_field(schema, "name"); TEST_ASSERT(name_found != NULL); TEST_ASSERT(strcmp(zvec_field_schema_get_name(name_found), "name") == 0); const zvec_field_schema_t *age_found = zvec_collection_schema_get_field(schema, "age"); TEST_ASSERT(age_found != NULL); TEST_ASSERT(strcmp(zvec_field_schema_get_name(age_found), "age") == 0); // Clean up fields we created zvec_field_schema_destroy(name_field); zvec_field_schema_destroy(age_field); zvec_field_schema_destroy(vec_field); // Test 8: Setting and getting max doc count err = zvec_collection_schema_set_max_doc_count_per_segment(schema, 10000); TEST_ASSERT(err == ZVEC_OK); uint64_t max_doc_count = zvec_collection_schema_get_max_doc_count_per_segment(schema); TEST_ASSERT(max_doc_count == 10000); // Test 9: Schema validation zvec_string_t *validation_error = NULL; err = zvec_collection_schema_validate(schema, &validation_error); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(validation_error == NULL); // Test 10: Removing single field err = zvec_collection_schema_drop_field(schema, "age"); TEST_ASSERT(err == ZVEC_OK); size_t count_after_remove = get_field_count(schema); TEST_ASSERT(count_after_remove == 3); // id, name, embedding const zvec_field_schema_t *removed_field = zvec_collection_schema_get_field(schema, "age"); TEST_ASSERT(removed_field == NULL); // Test 11: Removing multiple fields (keep embedding for validation) // remove_fields has been removed, use drop_field in a loop instead err = zvec_collection_schema_drop_field(schema, "name"); TEST_ASSERT(err == ZVEC_OK); err = zvec_collection_schema_drop_field(schema, "id"); TEST_ASSERT(err == ZVEC_OK); size_t final_count = get_field_count(schema); TEST_ASSERT(final_count == 1); // Only embedding remains // Test 12: Schema cleanup zvec_collection_schema_destroy(schema); TEST_END(); } void test_schema_edge_cases(void) { TEST_START(); // Test 1: NULL parameter handling for schema creation zvec_collection_schema_t *null_schema = zvec_collection_schema_create(NULL); TEST_ASSERT(null_schema == NULL); // Test 2: Empty string schema name zvec_collection_schema_t *empty_schema = zvec_collection_schema_create(""); TEST_ASSERT(empty_schema != NULL); TEST_ASSERT(zvec_collection_schema_get_name(empty_schema) != NULL); TEST_ASSERT(strcmp(zvec_collection_schema_get_name(empty_schema), "") == 0); zvec_collection_schema_destroy(empty_schema); // Test 3: Very long schema name char long_name[1024]; memset(long_name, 'a', 1023); long_name[1023] = '\0'; zvec_collection_schema_t *long_schema = zvec_collection_schema_create(long_name); TEST_ASSERT(long_schema != NULL); TEST_ASSERT(zvec_collection_schema_get_name(long_schema) != NULL); TEST_ASSERT(strlen(zvec_collection_schema_get_name(long_schema)) == 1023); zvec_collection_schema_destroy(long_schema); // Test 4: NULL schema parameter handling for all functions zvec_error_code_t err; const char **test_names = NULL; size_t test_count = 0; err = zvec_collection_schema_get_all_field_names(NULL, &test_names, &test_count); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); TEST_ASSERT(test_count == 0); const zvec_field_schema_t *null_field = zvec_collection_schema_get_field(NULL, "test"); TEST_ASSERT(null_field == NULL); zvec_field_schema_t *null_indexed_field = zvec_collection_schema_get_field(NULL, "test"); TEST_ASSERT(null_indexed_field == NULL); uint64_t null_max_doc_count = zvec_collection_schema_get_max_doc_count_per_segment(NULL); TEST_ASSERT(null_max_doc_count == 0); err = zvec_collection_schema_set_max_doc_count_per_segment(NULL, 1000); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); zvec_string_t *null_validation_error = NULL; err = zvec_collection_schema_validate(NULL, &null_validation_error); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); TEST_ASSERT(null_validation_error == NULL); err = zvec_collection_schema_add_field(NULL, NULL); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); err = zvec_collection_schema_drop_field(NULL, "test"); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); const char *null_field_names[] = {NULL}; // remove_fields has been removed, use drop_field in a loop instead for (int i = 0; i < 1; i++) { if (null_field_names[i]) { err = zvec_collection_schema_drop_field(NULL, null_field_names[i]); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); } } // Test 5: Working with valid schema for edge cases zvec_collection_schema_t *schema = zvec_collection_schema_create("edge_test"); TEST_ASSERT(schema != NULL); // Test 6: Adding NULL field to schema err = zvec_collection_schema_add_field(schema, NULL); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); // Test 7: Adding field with NULL name zvec_field_schema_t *null_name_field_schema = zvec_field_schema_create(NULL, ZVEC_DATA_TYPE_INT32, false, 0); err = zvec_collection_schema_add_field(schema, null_name_field_schema); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); zvec_field_schema_destroy(null_name_field_schema); // Test 8: Finding field with NULL name const zvec_field_schema_t *null_name_field = zvec_collection_schema_get_field(schema, NULL); TEST_ASSERT(null_name_field == NULL); // Test 9: Finding non-existent field const zvec_field_schema_t *nonexistent_field = zvec_collection_schema_get_field(schema, "nonexistent"); TEST_ASSERT(nonexistent_field == NULL); // Test 10: Getting field from empty schema const char **field_names = NULL; size_t field_count = 0; err = zvec_collection_schema_get_all_field_names(schema, &field_names, &field_count); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(field_count == 0); if (field_names) zvec_free(field_names); // Test 11: Removing field with NULL name err = zvec_collection_schema_drop_field(schema, NULL); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); // Test 12: Removing non-existent field err = zvec_collection_schema_drop_field(schema, "nonexistent"); TEST_ASSERT(err == ZVEC_ERROR_NOT_FOUND); // Test 13: Removing fields with NULL array - use drop_field in a loop const char *null_test_names[] = {"field1", "field2", "field3", "field4", "field5"}; for (int i = 0; i < 5; i++) { err = zvec_collection_schema_drop_field(schema, null_test_names[i]); // Expected to fail with NOT_FOUND since fields don't exist TEST_ASSERT(err == ZVEC_ERROR_NOT_FOUND || err == ZVEC_ERROR_INVALID_ARGUMENT); } // Test 16: Removing zero fields - nothing to test since remove_fields is // removed Just skip this test as it was specific to remove_fields function // Test 17: Setting extremely large max doc count err = zvec_collection_schema_set_max_doc_count_per_segment(schema, UINT64_MAX); TEST_ASSERT(err == ZVEC_OK); uint64_t retrieved_max_count = zvec_collection_schema_get_max_doc_count_per_segment(schema); TEST_ASSERT(retrieved_max_count == UINT64_MAX); // Test 18: Setting zero max doc count err = zvec_collection_schema_set_max_doc_count_per_segment(schema, 0); TEST_ASSERT(err == ZVEC_OK); uint64_t zero_max_count = zvec_collection_schema_get_max_doc_count_per_segment(schema); TEST_ASSERT(zero_max_count == 0); // Test 19: Schema validation with empty schema zvec_string_t *empty_validation_error = NULL; err = zvec_collection_schema_validate(schema, &empty_validation_error); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); // Test 20: Add duplicate field names zvec_field_schema_t *first_id = zvec_field_schema_create("duplicate_id", ZVEC_DATA_TYPE_INT64, false, 0); zvec_field_schema_t *second_id = zvec_field_schema_create("duplicate_id", ZVEC_DATA_TYPE_STRING, false, 0); err = zvec_collection_schema_add_field(schema, first_id); TEST_ASSERT(err == ZVEC_OK); err = zvec_collection_schema_add_field(schema, second_id); TEST_ASSERT(err == ZVEC_ERROR_ALREADY_EXISTS); zvec_field_schema_destroy(second_id); // Verify fields size_t verify_field_count = get_field_count(schema); TEST_ASSERT(verify_field_count == 1); // Test 21: Cleanup zvec_collection_schema_destroy(schema); TEST_END(); } void test_schema_field_operations(void) { TEST_START(); zvec_collection_schema_t *schema = zvec_test_create_temp_schema(); TEST_ASSERT(schema != NULL); if (schema) { // Test field count size_t initial_count = get_field_count(schema); TEST_ASSERT(initial_count == 5); // Test finding non-existent field const zvec_field_schema_t *nonexistent = zvec_collection_schema_get_field(schema, "nonexistent"); TEST_ASSERT(nonexistent == NULL); // Test finding existing field const zvec_field_schema_t *id_field = zvec_collection_schema_get_field(schema, "id"); TEST_ASSERT(id_field != NULL); if (id_field) { TEST_ASSERT(strcmp(zvec_field_schema_get_name(id_field), "id") == 0); TEST_ASSERT(zvec_field_schema_get_data_type(id_field) == ZVEC_DATA_TYPE_INT64); } zvec_collection_schema_destroy(schema); } TEST_END(); } void test_normal_schema_creation(void) { TEST_START(); zvec_collection_schema_t *schema = zvec_test_create_normal_schema(false, "test_normal", NULL, NULL, 1000); TEST_ASSERT(schema != NULL); if (schema) { TEST_ASSERT( strcmp(zvec_collection_schema_get_name(schema), "test_normal") == 0); // Verify field count size_t field_count = get_field_count(schema); TEST_ASSERT(field_count > 0); zvec_collection_schema_destroy(schema); } TEST_END(); } void test_schema_with_indexes(void) { TEST_START(); // Test Schema with scalar index zvec_collection_schema_t *scalar_index_schema = zvec_test_create_schema_with_scalar_index(true, true, "scalar_index_test"); TEST_ASSERT(scalar_index_schema != NULL); if (scalar_index_schema) { zvec_collection_schema_destroy(scalar_index_schema); } // Test Schema with vector index zvec_collection_schema_t *vector_index_schema = zvec_test_create_schema_with_vector_index(false, "vector_index_test", NULL); TEST_ASSERT(vector_index_schema != NULL); if (vector_index_schema) { zvec_collection_schema_destroy(vector_index_schema); } TEST_END(); } void test_schema_max_doc_count(void) { TEST_START(); // Test 1: Setting max doc count to a valid value zvec_collection_schema_t *schema = zvec_collection_schema_create("max_doc_test"); TEST_ASSERT(schema != NULL); zvec_error_code_t err = zvec_collection_schema_set_max_doc_count_per_segment(schema, 1000); TEST_ASSERT(err == ZVEC_OK); uint64_t max_doc_count = zvec_collection_schema_get_max_doc_count_per_segment(schema); TEST_ASSERT(max_doc_count == 1000); zvec_collection_schema_destroy(schema); // Test 2: Setting max doc count to zero schema = zvec_collection_schema_create("max_doc_test"); TEST_ASSERT(schema != NULL); err = zvec_collection_schema_set_max_doc_count_per_segment(schema, 0); TEST_ASSERT(err == ZVEC_OK); max_doc_count = zvec_collection_schema_get_max_doc_count_per_segment(schema); TEST_ASSERT(max_doc_count == 0); zvec_collection_schema_destroy(schema); // Test 3: Setting max doc count to maximum value schema = zvec_collection_schema_create("max_doc_test"); TEST_ASSERT(schema != NULL); err = zvec_collection_schema_set_max_doc_count_per_segment(schema, UINT64_MAX); TEST_ASSERT(err == ZVEC_OK); max_doc_count = zvec_collection_schema_get_max_doc_count_per_segment(schema); TEST_ASSERT(max_doc_count == UINT64_MAX); zvec_collection_schema_destroy(schema); TEST_END(); } void test_collection_schema_helpers(void) { TEST_START(); // Create schema with various field types zvec_collection_schema_t *schema = zvec_collection_schema_create("helper_test"); TEST_ASSERT(schema != NULL); if (schema) { // Add scalar fields zvec_field_schema_t *int_field = zvec_field_schema_create("int_field", ZVEC_DATA_TYPE_INT32, false, 0); zvec_field_schema_t *str_field = zvec_field_schema_create("str_field", ZVEC_DATA_TYPE_STRING, true, 0); // Add vector field zvec_field_schema_t *vec_field = zvec_field_schema_create( "vec_field", ZVEC_DATA_TYPE_VECTOR_FP32, false, 128); zvec_collection_schema_add_field(schema, int_field); zvec_collection_schema_add_field(schema, str_field); zvec_collection_schema_add_field(schema, vec_field); // Test has_field TEST_ASSERT(zvec_collection_schema_has_field(schema, "int_field") == true); TEST_ASSERT(zvec_collection_schema_has_field(schema, "str_field") == true); TEST_ASSERT(zvec_collection_schema_has_field(schema, "vec_field") == true); TEST_ASSERT(zvec_collection_schema_has_field(schema, "nonexistent") == false); // Test get_forward_field (scalar field) zvec_field_schema_t *found_int = zvec_collection_schema_get_forward_field(schema, "int_field"); TEST_ASSERT(found_int != NULL); TEST_ASSERT(zvec_field_schema_get_data_type(found_int) == ZVEC_DATA_TYPE_INT32); // get_forward_field should return NULL for vector field zvec_field_schema_t *vec_as_forward = zvec_collection_schema_get_forward_field(schema, "vec_field"); TEST_ASSERT(vec_as_forward == NULL); // Test get_vector_field zvec_field_schema_t *found_vec = zvec_collection_schema_get_vector_field(schema, "vec_field"); TEST_ASSERT(found_vec != NULL); TEST_ASSERT(zvec_field_schema_is_vector_field(found_vec) == true); // get_vector_field should return NULL for scalar field zvec_field_schema_t *int_as_vec = zvec_collection_schema_get_vector_field(schema, "int_field"); TEST_ASSERT(int_as_vec == NULL); // Test get_all_field_names const char **names = NULL; size_t name_count = 0; zvec_error_code_t err = zvec_collection_schema_get_all_field_names(schema, &names, &name_count); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(name_count == 3); // Free the strings (caller owns them) for (size_t i = 0; i < name_count; i++) { zvec_free((char *)names[i]); } zvec_free(names); // Test get_forward_fields zvec_field_schema_t **forward_fields = NULL; size_t forward_count = 0; err = zvec_collection_schema_get_forward_fields(schema, &forward_fields, &forward_count); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(forward_count == 2); // int_field and str_field // Note: forward_fields[i] are non-owning pointers, only free the array zvec_free(forward_fields); // Test get_vector_fields zvec_field_schema_t **vector_fields = NULL; size_t vector_count = 0; err = zvec_collection_schema_get_vector_fields(schema, &vector_fields, &vector_count); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(vector_count == 1); // vec_field // Note: vector_fields[i] are non-owning pointers, only free the array zvec_free(vector_fields); // Test has_index (initially no fields have index) TEST_ASSERT(zvec_collection_schema_has_index(schema, "int_field") == false); TEST_ASSERT(zvec_collection_schema_has_index(schema, "str_field") == false); TEST_ASSERT(zvec_collection_schema_has_index(schema, "vec_field") == false); // Test add_index zvec_index_params_t *invert_params = zvec_index_params_create(ZVEC_INDEX_TYPE_INVERT); TEST_ASSERT(invert_params != NULL); err = zvec_collection_schema_add_index(schema, "int_field", invert_params); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(zvec_collection_schema_has_index(schema, "int_field") == true); // Test drop_index err = zvec_collection_schema_drop_index(schema, "int_field"); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(zvec_collection_schema_has_index(schema, "int_field") == false); zvec_index_params_destroy(invert_params); zvec_collection_schema_destroy(schema); } TEST_END(); } void test_collection_schema_alter_field(void) { TEST_START(); zvec_collection_schema_t *schema = zvec_collection_schema_create("alter_test"); TEST_ASSERT(schema != NULL); if (schema) { // Create initial field zvec_field_schema_t *field = zvec_field_schema_create("test_field", ZVEC_DATA_TYPE_INT32, false, 0); TEST_ASSERT(field != NULL); zvec_error_code_t err = zvec_collection_schema_add_field(schema, field); TEST_ASSERT(err == ZVEC_OK); // Verify initial state const zvec_field_schema_t *found = zvec_collection_schema_get_field(schema, "test_field"); TEST_ASSERT(found != NULL); TEST_ASSERT(zvec_field_schema_is_nullable(found) == false); // Alter the field to make it nullable zvec_field_schema_t *new_field = zvec_field_schema_create("test_field", ZVEC_DATA_TYPE_INT32, true, 0); TEST_ASSERT(new_field != NULL); err = zvec_collection_schema_alter_field(schema, "test_field", new_field); TEST_ASSERT(err == ZVEC_OK); // Verify the change found = zvec_collection_schema_get_field(schema, "test_field"); TEST_ASSERT(found != NULL); TEST_ASSERT(zvec_field_schema_is_nullable(found) == true); // Test alter non-existent field err = zvec_collection_schema_alter_field(schema, "nonexistent", new_field); TEST_ASSERT(err != ZVEC_OK); zvec_field_schema_destroy(new_field); zvec_collection_schema_destroy(schema); } TEST_END(); } // ============================================================================= // Collection-related tests // ============================================================================= void test_collection_basic_operations(void) { TEST_START(); // Create temporary directory char temp_dir[] = "./zvec_test_collection_basic_operations"; zvec_collection_schema_t *schema = zvec_test_create_temp_schema(); TEST_ASSERT(schema != NULL); if (schema) { zvec_collection_t *collection = NULL; zvec_error_code_t err = zvec_collection_create_and_open(temp_dir, schema, NULL, &collection); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(collection != NULL); if (collection) { // Test collection operations zvec_doc_t *doc1 = zvec_test_create_doc(1, schema, NULL); zvec_doc_t *doc2 = zvec_test_create_doc(2, schema, NULL); zvec_doc_t *doc3 = zvec_test_create_doc(3, schema, NULL); TEST_ASSERT(doc1 != NULL); TEST_ASSERT(doc2 != NULL); TEST_ASSERT(doc3 != NULL); if (doc1 && doc2 && doc3) { zvec_doc_t *docs[] = {doc1, doc2, doc3}; size_t success_count, error_count; // Test insert operation err = zvec_collection_insert(collection, (const zvec_doc_t **)docs, 3, &success_count, &error_count); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(success_count == 3); TEST_ASSERT(error_count == 0); // Test update operation zvec_doc_set_score(doc1, 0.95f); zvec_doc_t *update_docs[] = {doc1}; err = zvec_collection_update(collection, (const zvec_doc_t **)update_docs, 1, &success_count, &error_count); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(success_count == 1); TEST_ASSERT(error_count == 0); // Test upsert operation zvec_doc_set_pk(doc3, "pk_3_modified"); zvec_doc_t *upsert_docs[] = {doc3}; err = zvec_collection_upsert(collection, (const zvec_doc_t **)upsert_docs, 1, &success_count, &error_count); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(success_count == 1); TEST_ASSERT(error_count == 0); // Test delete operation by primary keys const char *pks[] = {"pk_1", "pk_2"}; err = zvec_collection_delete(collection, pks, 2, &success_count, &error_count); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(success_count == 2); TEST_ASSERT(error_count == 0); // Test delete by filter err = zvec_collection_delete_by_filter(collection, "id > 0"); TEST_ASSERT(err == ZVEC_OK); // Clean up documents zvec_doc_destroy(doc1); zvec_doc_destroy(doc2); zvec_doc_destroy(doc3); } // Test collection flush err = zvec_collection_flush(collection); TEST_ASSERT(err == ZVEC_OK); // Test collection optimization err = zvec_collection_optimize(collection); TEST_ASSERT(err == ZVEC_OK); zvec_collection_destroy(collection); } zvec_collection_schema_destroy(schema); } // Clean up temporary directory cleanup_temp_directory(temp_dir); TEST_END(); } void test_collection_edge_cases(void) { TEST_START(); char temp_dir[] = "./zvec_test_collection_edge_cases"; zvec_collection_schema_t *schema = zvec_test_create_temp_schema(); TEST_ASSERT(schema != NULL); if (schema) { zvec_collection_t *collection = NULL; // Test empty name collection zvec_error_code_t err = zvec_collection_create_and_open(temp_dir, schema, NULL, &collection); TEST_ASSERT(err == ZVEC_OK); if (collection) { zvec_collection_destroy(collection); collection = NULL; } // Test long name collection char long_name[256]; memset(long_name, 'a', 255); long_name[255] = '\0'; char long_path[512]; snprintf(long_path, sizeof(long_path), "%s/%s", temp_dir, "very_long_collection_name_that_tests_path_limits"); err = zvec_collection_create_and_open(long_path, schema, NULL, &collection); TEST_ASSERT(err == ZVEC_OK); if (collection) { zvec_collection_destroy(collection); collection = NULL; } // Test NULL name集合 err = zvec_collection_create_and_open(temp_dir, schema, NULL, &collection); TEST_ASSERT(err != ZVEC_OK); zvec_collection_schema_destroy(schema); } // Clean up temporary directory cleanup_temp_directory(temp_dir); TEST_END(); } void test_collection_delete_by_filter(void) { TEST_START(); char temp_dir[] = "./zvec_test_collection_delete_by_filter"; zvec_collection_schema_t *schema = zvec_test_create_temp_schema(); TEST_ASSERT(schema != NULL); if (schema) { zvec_collection_t *collection = NULL; zvec_error_code_t err = zvec_collection_create_and_open(temp_dir, schema, NULL, &collection); TEST_ASSERT(err == ZVEC_OK); if (collection) { // Test normal deletion filtering err = zvec_collection_delete_by_filter(collection, "id > 1"); TEST_ASSERT(err == ZVEC_OK); // Test NULL filter err = zvec_collection_delete_by_filter(collection, NULL); TEST_ASSERT(err != ZVEC_OK); // Test empty string filter err = zvec_collection_delete_by_filter(collection, ""); TEST_ASSERT(err == ZVEC_OK); zvec_collection_destroy(collection); } zvec_collection_schema_destroy(schema); } // Clean up temporary directory cleanup_temp_directory(temp_dir); TEST_END(); } void test_collection_stats(void) { TEST_START(); char temp_dir[] = "./zvec_test_collection_stats"; zvec_collection_schema_t *schema = zvec_test_create_temp_schema(); TEST_ASSERT(schema != NULL); if (schema) { zvec_collection_t *collection = NULL; zvec_error_code_t err = zvec_collection_create_and_open(temp_dir, schema, NULL, &collection); TEST_ASSERT(err == ZVEC_OK); if (collection) { zvec_collection_stats_t *stats = NULL; err = zvec_collection_get_stats(collection, &stats); TEST_ASSERT(err == ZVEC_OK); if (stats) { // Basic validation of statistics TEST_ASSERT(zvec_collection_stats_get_doc_count(stats) == 0); // New collection should have no documents zvec_collection_stats_destroy(stats); } zvec_collection_destroy(collection); } zvec_collection_schema_destroy(schema); } // Clean up temporary directory cleanup_temp_directory(temp_dir); TEST_END(); } // ============================================================================= // Field-related tests // ============================================================================= void test_field_schema_functions(void) { TEST_START(); // Test scalar field creation using API zvec_field_schema_t *scalar_field = zvec_field_schema_create("test_field", ZVEC_DATA_TYPE_STRING, true, 0); TEST_ASSERT(scalar_field != NULL); if (scalar_field) { TEST_ASSERT( strcmp(zvec_field_schema_get_name(scalar_field), "test_field") == 0); TEST_ASSERT(zvec_field_schema_get_data_type(scalar_field) == ZVEC_DATA_TYPE_STRING); TEST_ASSERT(zvec_field_schema_is_nullable(scalar_field) == true); TEST_ASSERT(zvec_field_schema_get_dimension(scalar_field) == 0); // Test new functions for scalar field TEST_ASSERT(zvec_field_schema_is_vector_field(scalar_field) == false); TEST_ASSERT(zvec_field_schema_is_dense_vector(scalar_field) == false); TEST_ASSERT(zvec_field_schema_is_sparse_vector(scalar_field) == false); TEST_ASSERT(zvec_field_schema_is_array_type(scalar_field) == false); TEST_ASSERT(zvec_field_schema_get_element_data_type(scalar_field) == ZVEC_DATA_TYPE_STRING); TEST_ASSERT(zvec_field_schema_has_invert_index(scalar_field) == false); TEST_ASSERT(zvec_field_schema_get_index_type(scalar_field) == ZVEC_INDEX_TYPE_UNDEFINED); zvec_field_schema_destroy(scalar_field); } // Test vector field creation using API zvec_field_schema_t *vector_field = zvec_field_schema_create( "vec_field", ZVEC_DATA_TYPE_VECTOR_FP32, false, 128); TEST_ASSERT(vector_field != NULL); if (vector_field) { TEST_ASSERT(strcmp(zvec_field_schema_get_name(vector_field), "vec_field") == 0); TEST_ASSERT(zvec_field_schema_get_data_type(vector_field) == ZVEC_DATA_TYPE_VECTOR_FP32); TEST_ASSERT(zvec_field_schema_is_nullable(vector_field) == false); TEST_ASSERT(zvec_field_schema_get_dimension(vector_field) == 128); // Test new functions for dense vector field TEST_ASSERT(zvec_field_schema_is_vector_field(vector_field) == true); TEST_ASSERT(zvec_field_schema_is_dense_vector(vector_field) == true); TEST_ASSERT(zvec_field_schema_is_sparse_vector(vector_field) == false); TEST_ASSERT(zvec_field_schema_is_array_type(vector_field) == false); zvec_field_schema_destroy(vector_field); } // Test sparse vector field creation using API zvec_field_schema_t *sparse_field = zvec_field_schema_create( "sparse_field", ZVEC_DATA_TYPE_SPARSE_VECTOR_FP32, false, 0); TEST_ASSERT(sparse_field != NULL); if (sparse_field) { TEST_ASSERT( strcmp(zvec_field_schema_get_name(sparse_field), "sparse_field") == 0); TEST_ASSERT(zvec_field_schema_get_data_type(sparse_field) == ZVEC_DATA_TYPE_SPARSE_VECTOR_FP32); // Test new functions for sparse vector field TEST_ASSERT(zvec_field_schema_is_vector_field(sparse_field) == true); TEST_ASSERT(zvec_field_schema_is_dense_vector(sparse_field) == false); TEST_ASSERT(zvec_field_schema_is_sparse_vector(sparse_field) == true); zvec_field_schema_destroy(sparse_field); } // Test array field zvec_field_schema_t *array_field = zvec_field_schema_create( "array_field", ZVEC_DATA_TYPE_ARRAY_INT32, false, 0); TEST_ASSERT(array_field != NULL); if (array_field) { TEST_ASSERT(zvec_field_schema_is_array_type(array_field) == true); TEST_ASSERT(zvec_field_schema_is_vector_field(array_field) == false); TEST_ASSERT(zvec_field_schema_get_element_data_type(array_field) == ZVEC_DATA_TYPE_INT32); zvec_field_schema_destroy(array_field); } // Test field with invert index zvec_index_params_t *invert_params = zvec_index_params_create(ZVEC_INDEX_TYPE_INVERT); zvec_index_params_set_metric_type(invert_params, ZVEC_METRIC_TYPE_L2); zvec_index_params_set_invert_params(invert_params, true, false); zvec_field_schema_t *indexed_field = zvec_field_schema_create("indexed_field", ZVEC_DATA_TYPE_INT64, false, 0); TEST_ASSERT(indexed_field != NULL); if (indexed_field) { zvec_field_schema_set_index_params(indexed_field, invert_params); TEST_ASSERT(zvec_field_schema_has_index(indexed_field) == true); TEST_ASSERT(zvec_field_schema_get_index_type(indexed_field) == ZVEC_INDEX_TYPE_INVERT); TEST_ASSERT(zvec_field_schema_has_invert_index(indexed_field) == true); zvec_field_schema_destroy(indexed_field); } zvec_index_params_destroy(invert_params); // Test field with HNSW index zvec_index_params_t *hnsw_params = zvec_index_params_create(ZVEC_INDEX_TYPE_HNSW); zvec_index_params_set_metric_type(hnsw_params, ZVEC_METRIC_TYPE_L2); zvec_index_params_set_hnsw_params(hnsw_params, 16, 200); zvec_field_schema_t *hnsw_field = zvec_field_schema_create( "hnsw_field", ZVEC_DATA_TYPE_VECTOR_FP32, false, 128); TEST_ASSERT(hnsw_field != NULL); if (hnsw_field) { zvec_field_schema_set_index_params(hnsw_field, hnsw_params); TEST_ASSERT(zvec_field_schema_has_index(hnsw_field) == true); TEST_ASSERT(zvec_field_schema_get_index_type(hnsw_field) == ZVEC_INDEX_TYPE_HNSW); TEST_ASSERT(zvec_field_schema_has_invert_index(hnsw_field) == false); // Vector field, no invert index zvec_field_schema_destroy(hnsw_field); } zvec_index_params_destroy(hnsw_params); TEST_END(); } void test_field_helper_functions(void) { TEST_START(); // Test scalar field helper functions zvec_index_params_t *invert_params = zvec_test_create_default_invert_params(true); zvec_field_schema_t *scalar_field = zvec_test_create_scalar_field( "test_scalar", ZVEC_DATA_TYPE_INT32, true, invert_params); TEST_ASSERT(scalar_field != NULL); if (scalar_field) { TEST_ASSERT( strcmp(zvec_field_schema_get_name(scalar_field), "test_scalar") == 0); TEST_ASSERT(zvec_field_schema_get_data_type(scalar_field) == ZVEC_DATA_TYPE_INT32); zvec_field_schema_destroy(scalar_field); } zvec_index_params_destroy(invert_params); // Test vector field helper functions zvec_index_params_t *hnsw_params = zvec_test_create_default_hnsw_params(); zvec_field_schema_t *vector_field = zvec_test_create_vector_field( "test_vector", ZVEC_DATA_TYPE_VECTOR_FP32, 128, false, hnsw_params); TEST_ASSERT(vector_field != NULL); if (vector_field) { TEST_ASSERT( strcmp(zvec_field_schema_get_name(vector_field), "test_vector") == 0); TEST_ASSERT(zvec_field_schema_get_data_type(vector_field) == ZVEC_DATA_TYPE_VECTOR_FP32); TEST_ASSERT(zvec_field_schema_get_dimension(vector_field) == 128); zvec_field_schema_destroy(vector_field); } zvec_index_params_destroy(hnsw_params); TEST_END(); } // ============================================================================= // Document-related tests // ============================================================================= void test_doc_creation(void) { TEST_START(); zvec_collection_schema_t *schema = zvec_test_create_temp_schema(); TEST_ASSERT(schema != NULL); if (schema) { // Test complete document creation zvec_doc_t *doc = zvec_test_create_doc(1, schema, NULL); TEST_ASSERT(doc != NULL); if (doc) { zvec_doc_destroy(doc); } // Test null value document creation zvec_doc_t *null_doc = zvec_test_create_doc_null(2, schema, NULL); TEST_ASSERT(null_doc != NULL); if (null_doc) { zvec_doc_destroy(null_doc); } zvec_collection_schema_destroy(schema); } TEST_END(); } void test_doc_primary_key(void) { TEST_START(); // Test primary key generation char *pk = zvec_test_make_pk(12345); TEST_ASSERT(pk != NULL); if (pk) { TEST_ASSERT(strcmp(pk, "pk_12345") == 0); zvec_free(pk); } TEST_END(); } // Test for zvec_doc_add_field_by_value - covers all data types void test_doc_add_field_by_value(void) { TEST_START(); zvec_doc_t *doc = zvec_doc_create(); TEST_ASSERT(doc != NULL); if (!doc) { TEST_END(); return; } // Scalar types // BINARY const char *binary_data = "binary"; zvec_error_code_t err = zvec_doc_add_field_by_value(doc, "binary_field", ZVEC_DATA_TYPE_BINARY, binary_data, strlen(binary_data)); TEST_ASSERT(err == ZVEC_OK); // STRING const char *string_data = "hello"; err = zvec_doc_add_field_by_value(doc, "string_field", ZVEC_DATA_TYPE_STRING, string_data, strlen(string_data)); TEST_ASSERT(err == ZVEC_OK); // BOOL bool bool_val = true; err = zvec_doc_add_field_by_value(doc, "bool_field", ZVEC_DATA_TYPE_BOOL, &bool_val, sizeof(bool_val)); TEST_ASSERT(err == ZVEC_OK); // INT32 int32_t int32_val = -12345; err = zvec_doc_add_field_by_value(doc, "int32_field", ZVEC_DATA_TYPE_INT32, &int32_val, sizeof(int32_val)); TEST_ASSERT(err == ZVEC_OK); // INT64 int64_t int64_val = -9876543210LL; err = zvec_doc_add_field_by_value(doc, "int64_field", ZVEC_DATA_TYPE_INT64, &int64_val, sizeof(int64_val)); TEST_ASSERT(err == ZVEC_OK); // UINT32 uint32_t uint32_val = 4294967295U; err = zvec_doc_add_field_by_value(doc, "uint32_field", ZVEC_DATA_TYPE_UINT32, &uint32_val, sizeof(uint32_val)); TEST_ASSERT(err == ZVEC_OK); // UINT64 uint64_t uint64_val = 18446744073709551615ULL; err = zvec_doc_add_field_by_value(doc, "uint64_field", ZVEC_DATA_TYPE_UINT64, &uint64_val, sizeof(uint64_val)); TEST_ASSERT(err == ZVEC_OK); // FLOAT float float_val = 3.14159f; err = zvec_doc_add_field_by_value(doc, "float_field", ZVEC_DATA_TYPE_FLOAT, &float_val, sizeof(float_val)); TEST_ASSERT(err == ZVEC_OK); // DOUBLE double double_val = 3.14159265358979; err = zvec_doc_add_field_by_value(doc, "double_field", ZVEC_DATA_TYPE_DOUBLE, &double_val, sizeof(double_val)); TEST_ASSERT(err == ZVEC_OK); // Vector types // VECTOR_BINARY32 uint32_t binary32_vec[] = {0xFFFFFFFF, 0x00000000, 0xAAAAAAAA, 0x55555555}; err = zvec_doc_add_field_by_value(doc, "binary32_vec_field", ZVEC_DATA_TYPE_VECTOR_BINARY32, binary32_vec, sizeof(binary32_vec)); TEST_ASSERT(err == ZVEC_OK); // VECTOR_BINARY64 uint64_t binary64_vec[] = {0xFFFFFFFFFFFFFFFFULL, 0x0000000000000000ULL}; err = zvec_doc_add_field_by_value(doc, "binary64_vec_field", ZVEC_DATA_TYPE_VECTOR_BINARY64, binary64_vec, sizeof(binary64_vec)); TEST_ASSERT(err == ZVEC_OK); // VECTOR_FP16 uint16_t fp16_vec[] = {0x3C00, 0x4000, 0xC000, 0x8000}; err = zvec_doc_add_field_by_value(doc, "fp16_vec_field", ZVEC_DATA_TYPE_VECTOR_FP16, fp16_vec, sizeof(fp16_vec)); TEST_ASSERT(err == ZVEC_OK); // VECTOR_FP32 float fp32_vec[] = {1.0f, -2.0f, 3.5f, -4.5f}; err = zvec_doc_add_field_by_value(doc, "fp32_vec_field", ZVEC_DATA_TYPE_VECTOR_FP32, fp32_vec, sizeof(fp32_vec)); TEST_ASSERT(err == ZVEC_OK); // VECTOR_FP64 double fp64_vec[] = {1.1, -2.2, 3.3, -4.4}; err = zvec_doc_add_field_by_value(doc, "fp64_vec_field", ZVEC_DATA_TYPE_VECTOR_FP64, fp64_vec, sizeof(fp64_vec)); TEST_ASSERT(err == ZVEC_OK); // VECTOR_INT4 (packed - each byte contains 2 values) int8_t int4_vec[] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0}; err = zvec_doc_add_field_by_value(doc, "int4_vec_field", ZVEC_DATA_TYPE_VECTOR_INT4, int4_vec, sizeof(int4_vec)); TEST_ASSERT(err == ZVEC_OK); // VECTOR_INT8 int8_t int8_vec[] = {-128, -1, 0, 1, 127}; err = zvec_doc_add_field_by_value(doc, "int8_vec_field", ZVEC_DATA_TYPE_VECTOR_INT8, int8_vec, sizeof(int8_vec)); TEST_ASSERT(err == ZVEC_OK); // VECTOR_INT16 int16_t int16_vec[] = {-32768, -1, 0, 1, 32767}; err = zvec_doc_add_field_by_value(doc, "int16_vec_field", ZVEC_DATA_TYPE_VECTOR_INT16, int16_vec, sizeof(int16_vec)); TEST_ASSERT(err == ZVEC_OK); // Sparse vector types // SPARSE_VECTOR_FP16 - format: [nnz(size_t)][indices...][values...] size_t sparse_fp16_nnz = 3; uint32_t sparse_fp16_indices[] = {0, 5, 10}; uint16_t sparse_fp16_values[] = {0x3C00, 0x4000, 0xC000}; size_t sparse_fp16_size = sizeof(sparse_fp16_nnz) + sizeof(sparse_fp16_indices) + sizeof(sparse_fp16_values); char *sparse_fp16_buffer = (char *)malloc(sparse_fp16_size); memcpy(sparse_fp16_buffer, &sparse_fp16_nnz, sizeof(sparse_fp16_nnz)); memcpy(sparse_fp16_buffer + sizeof(sparse_fp16_nnz), sparse_fp16_indices, sizeof(sparse_fp16_indices)); memcpy(sparse_fp16_buffer + sizeof(sparse_fp16_nnz) + sizeof(sparse_fp16_indices), sparse_fp16_values, sizeof(sparse_fp16_values)); err = zvec_doc_add_field_by_value(doc, "sparse_fp16_field", ZVEC_DATA_TYPE_SPARSE_VECTOR_FP16, sparse_fp16_buffer, sparse_fp16_size); TEST_ASSERT(err == ZVEC_OK); free(sparse_fp16_buffer); // SPARSE_VECTOR_FP32 size_t sparse_fp32_nnz = 3; uint32_t sparse_fp32_indices[] = {2, 7, 15}; float sparse_fp32_values[] = {1.5f, -2.5f, 3.5f}; size_t sparse_fp32_size = sizeof(sparse_fp32_nnz) + sizeof(sparse_fp32_indices) + sizeof(sparse_fp32_values); char *sparse_fp32_buffer = (char *)malloc(sparse_fp32_size); memcpy(sparse_fp32_buffer, &sparse_fp32_nnz, sizeof(sparse_fp32_nnz)); memcpy(sparse_fp32_buffer + sizeof(sparse_fp32_nnz), sparse_fp32_indices, sizeof(sparse_fp32_indices)); memcpy(sparse_fp32_buffer + sizeof(sparse_fp32_nnz) + sizeof(sparse_fp32_indices), sparse_fp32_values, sizeof(sparse_fp32_values)); err = zvec_doc_add_field_by_value(doc, "sparse_fp32_field", ZVEC_DATA_TYPE_SPARSE_VECTOR_FP32, sparse_fp32_buffer, sparse_fp32_size); TEST_ASSERT(err == ZVEC_OK); free(sparse_fp32_buffer); // Array types // ARRAY_BINARY - format: [length(uint32_t)][data][length][data]... uint8_t array_bin_data[] = { 1, 0, 0, 0, 0x01, // length=1, data=0x01 2, 0, 0, 0, 0x02, 0x03, // length=2, data=0x02,0x03 2, 0, 0, 0, 0x04, 0x05 // length=2, data=0x04,0x05 }; err = zvec_doc_add_field_by_value(doc, "array_binary_field", ZVEC_DATA_TYPE_ARRAY_BINARY, array_bin_data, sizeof(array_bin_data)); TEST_ASSERT(err == ZVEC_OK); // ARRAY_STRING - null-terminated strings const char *array_str_data[] = {"str1", "str2", "str3"}; zvec_string_t *array_zvec_str[3]; for (int i = 0; i < 3; i++) { array_zvec_str[i] = zvec_string_create(array_str_data[i]); } err = zvec_doc_add_field_by_value(doc, "array_string_field", ZVEC_DATA_TYPE_ARRAY_STRING, array_zvec_str, sizeof(array_zvec_str)); TEST_ASSERT(err == ZVEC_OK); // ARRAY_BOOL bool array_bool_data[] = {true, false, true, false}; err = zvec_doc_add_field_by_value(doc, "array_bool_field", ZVEC_DATA_TYPE_ARRAY_BOOL, array_bool_data, sizeof(array_bool_data)); TEST_ASSERT(err == ZVEC_OK); // ARRAY_INT32 int32_t array_int32_data[] = {-100, -50, 0, 50, 100}; err = zvec_doc_add_field_by_value(doc, "array_int32_field", ZVEC_DATA_TYPE_ARRAY_INT32, array_int32_data, sizeof(array_int32_data)); TEST_ASSERT(err == ZVEC_OK); // ARRAY_INT64 int64_t array_int64_data[] = {-1000000, -500000, 0, 500000, 1000000}; err = zvec_doc_add_field_by_value(doc, "array_int64_field", ZVEC_DATA_TYPE_ARRAY_INT64, array_int64_data, sizeof(array_int64_data)); TEST_ASSERT(err == ZVEC_OK); // ARRAY_UINT32 uint32_t array_uint32_data[] = {0, 100, 1000, 10000, 4294967295U}; err = zvec_doc_add_field_by_value( doc, "array_uint32_field", ZVEC_DATA_TYPE_ARRAY_UINT32, array_uint32_data, sizeof(array_uint32_data)); TEST_ASSERT(err == ZVEC_OK); // ARRAY_UINT64 uint64_t array_uint64_data[] = {0, 100, 1000, 10000, 18446744073709551615ULL}; err = zvec_doc_add_field_by_value( doc, "array_uint64_field", ZVEC_DATA_TYPE_ARRAY_UINT64, array_uint64_data, sizeof(array_uint64_data)); TEST_ASSERT(err == ZVEC_OK); // ARRAY_FLOAT float array_float_data[] = {-1.5f, -0.5f, 0.0f, 0.5f, 1.5f}; err = zvec_doc_add_field_by_value(doc, "array_float_field", ZVEC_DATA_TYPE_ARRAY_FLOAT, array_float_data, sizeof(array_float_data)); TEST_ASSERT(err == ZVEC_OK); // ARRAY_DOUBLE double array_double_data[] = {-1.1, -0.1, 0.0, 0.1, 1.1}; err = zvec_doc_add_field_by_value( doc, "array_double_field", ZVEC_DATA_TYPE_ARRAY_DOUBLE, array_double_data, sizeof(array_double_data)); TEST_ASSERT(err == ZVEC_OK); // Verify we can retrieve some of the values void *result = NULL; size_t result_size = 0; err = zvec_doc_get_field_value_copy(doc, "int32_field", ZVEC_DATA_TYPE_INT32, &result, &result_size); TEST_ASSERT(err == ZVEC_OK && result_size == sizeof(int32_t)); if (result) { TEST_ASSERT(*(int32_t *)result == -12345); zvec_free(result); } err = zvec_doc_get_field_value_copy(doc, "float_field", ZVEC_DATA_TYPE_FLOAT, &result, &result_size); TEST_ASSERT(err == ZVEC_OK && result_size == sizeof(float)); if (result) { TEST_ASSERT(fabs(*(float *)result - 3.14159f) < 0.0001f); zvec_free(result); } zvec_doc_destroy(doc); TEST_END(); } // Test for zvec_doc_add_field_by_struct - covers all data types void test_doc_add_field_by_struct(void) { TEST_START(); zvec_doc_t *doc = zvec_doc_create(); TEST_ASSERT(doc != NULL); if (!doc) { TEST_END(); return; } zvec_error_code_t err; zvec_doc_field_t field; // Scalar types // BINARY memset(&field, 0, sizeof(field)); field.name.data = "binary_field"; field.name.length = strlen("binary_field"); field.data_type = ZVEC_DATA_TYPE_BINARY; uint8_t binary_data[] = {0x01, 0x02, 0x03, 0x04}; field.value.binary_value.data = binary_data; field.value.binary_value.length = sizeof(binary_data); err = zvec_doc_add_field_by_struct(doc, &field); TEST_ASSERT(err == ZVEC_OK); // STRING memset(&field, 0, sizeof(field)); field.name.data = "string_field"; field.name.length = strlen("string_field"); field.data_type = ZVEC_DATA_TYPE_STRING; const char *string_data = "hello world"; field.value.string_value.data = (char *)string_data; field.value.string_value.length = strlen(string_data); err = zvec_doc_add_field_by_struct(doc, &field); TEST_ASSERT(err == ZVEC_OK); // BOOL memset(&field, 0, sizeof(field)); field.name.data = "bool_field"; field.name.length = strlen("bool_field"); field.data_type = ZVEC_DATA_TYPE_BOOL; field.value.bool_value = true; err = zvec_doc_add_field_by_struct(doc, &field); TEST_ASSERT(err == ZVEC_OK); // INT32 memset(&field, 0, sizeof(field)); field.name.data = "int32_field"; field.name.length = strlen("int32_field"); field.data_type = ZVEC_DATA_TYPE_INT32; field.value.int32_value = -12345; err = zvec_doc_add_field_by_struct(doc, &field); TEST_ASSERT(err == ZVEC_OK); // INT64 memset(&field, 0, sizeof(field)); field.name.data = "int64_field"; field.name.length = strlen("int64_field"); field.data_type = ZVEC_DATA_TYPE_INT64; field.value.int64_value = -9876543210LL; err = zvec_doc_add_field_by_struct(doc, &field); TEST_ASSERT(err == ZVEC_OK); // UINT32 memset(&field, 0, sizeof(field)); field.name.data = "uint32_field"; field.name.length = strlen("uint32_field"); field.data_type = ZVEC_DATA_TYPE_UINT32; field.value.uint32_value = 4294967295U; err = zvec_doc_add_field_by_struct(doc, &field); TEST_ASSERT(err == ZVEC_OK); // UINT64 memset(&field, 0, sizeof(field)); field.name.data = "uint64_field"; field.name.length = strlen("uint64_field"); field.data_type = ZVEC_DATA_TYPE_UINT64; field.value.uint64_value = 18446744073709551615ULL; err = zvec_doc_add_field_by_struct(doc, &field); TEST_ASSERT(err == ZVEC_OK); // FLOAT memset(&field, 0, sizeof(field)); field.name.data = "float_field"; field.name.length = strlen("float_field"); field.data_type = ZVEC_DATA_TYPE_FLOAT; field.value.float_value = 3.14159f; err = zvec_doc_add_field_by_struct(doc, &field); TEST_ASSERT(err == ZVEC_OK); // DOUBLE memset(&field, 0, sizeof(field)); field.name.data = "double_field"; field.name.length = strlen("double_field"); field.data_type = ZVEC_DATA_TYPE_DOUBLE; field.value.double_value = 3.14159265358979; err = zvec_doc_add_field_by_struct(doc, &field); TEST_ASSERT(err == ZVEC_OK); // VECTOR_BINARY32 memset(&field, 0, sizeof(field)); field.name.data = "binary32_vec_field"; field.name.length = strlen("binary32_vec_field"); field.data_type = ZVEC_DATA_TYPE_VECTOR_BINARY32; uint32_t binary32_vec[] = {0xFFFFFFFF, 0x00000000, 0xAAAAAAAA, 0x55555555}; field.value.vector_value.data = (const float *)binary32_vec; field.value.vector_value.length = sizeof(binary32_vec) / sizeof(uint32_t); err = zvec_doc_add_field_by_struct(doc, &field); TEST_ASSERT(err == ZVEC_OK); // VECTOR_BINARY64 memset(&field, 0, sizeof(field)); field.name.data = "binary64_vec_field"; field.name.length = strlen("binary64_vec_field"); field.data_type = ZVEC_DATA_TYPE_VECTOR_BINARY64; uint64_t binary64_vec[] = {0xFFFFFFFFFFFFFFFFULL, 0x0000000000000000ULL}; field.value.vector_value.data = (const float *)binary64_vec; field.value.vector_value.length = sizeof(binary64_vec) / sizeof(uint64_t); err = zvec_doc_add_field_by_struct(doc, &field); TEST_ASSERT(err == ZVEC_OK); // VECTOR_FP16 memset(&field, 0, sizeof(field)); field.name.data = "fp16_vec_field"; field.name.length = strlen("fp16_vec_field"); field.data_type = ZVEC_DATA_TYPE_VECTOR_FP16; uint16_t fp16_vec[] = {0x3C00, 0x4000, 0xC000, 0x8000}; field.value.vector_value.data = (const float *)fp16_vec; field.value.vector_value.length = sizeof(fp16_vec) / sizeof(uint16_t); err = zvec_doc_add_field_by_struct(doc, &field); TEST_ASSERT(err == ZVEC_OK); // VECTOR_FP32 memset(&field, 0, sizeof(field)); field.name.data = "fp32_vec_field"; field.name.length = strlen("fp32_vec_field"); field.data_type = ZVEC_DATA_TYPE_VECTOR_FP32; float fp32_vec[] = {1.0f, -2.0f, 3.5f, -4.5f}; field.value.vector_value.data = fp32_vec; field.value.vector_value.length = sizeof(fp32_vec) / sizeof(float); err = zvec_doc_add_field_by_struct(doc, &field); TEST_ASSERT(err == ZVEC_OK); // VECTOR_FP64 memset(&field, 0, sizeof(field)); field.name.data = "fp64_vec_field"; field.name.length = strlen("fp64_vec_field"); field.data_type = ZVEC_DATA_TYPE_VECTOR_FP64; double fp64_vec[] = {1.1, -2.2, 3.3, -4.4}; field.value.vector_value.data = (const float *)fp64_vec; field.value.vector_value.length = sizeof(fp64_vec) / sizeof(double); err = zvec_doc_add_field_by_struct(doc, &field); TEST_ASSERT(err == ZVEC_OK); // VECTOR_INT4 memset(&field, 0, sizeof(field)); field.name.data = "int4_vec_field"; field.name.length = strlen("int4_vec_field"); field.data_type = ZVEC_DATA_TYPE_VECTOR_INT4; int8_t int4_vec[] = {0x12, 0x34, 0x56, 0x78}; field.value.vector_value.data = (const float *)int4_vec; field.value.vector_value.length = sizeof(int4_vec) * 2; // Each byte contains 2 values err = zvec_doc_add_field_by_struct(doc, &field); TEST_ASSERT(err == ZVEC_OK); // VECTOR_INT8 memset(&field, 0, sizeof(field)); field.name.data = "int8_vec_field"; field.name.length = strlen("int8_vec_field"); field.data_type = ZVEC_DATA_TYPE_VECTOR_INT8; int8_t int8_vec[] = {-128, -1, 0, 1, 127}; field.value.vector_value.data = (const float *)int8_vec; field.value.vector_value.length = sizeof(int8_vec) / sizeof(int8_t); err = zvec_doc_add_field_by_struct(doc, &field); TEST_ASSERT(err == ZVEC_OK); // VECTOR_INT16 memset(&field, 0, sizeof(field)); field.name.data = "int16_vec_field"; field.name.length = strlen("int16_vec_field"); field.data_type = ZVEC_DATA_TYPE_VECTOR_INT16; int16_t int16_vec[] = {-32768, -1, 0, 1, 32767}; field.value.vector_value.data = (const float *)int16_vec; field.value.vector_value.length = sizeof(int16_vec) / sizeof(int16_t); err = zvec_doc_add_field_by_struct(doc, &field); TEST_ASSERT(err == ZVEC_OK); // Sparse vector types // SPARSE_VECTOR_FP16 memset(&field, 0, sizeof(field)); field.name.data = "sparse_fp16_field"; field.name.length = strlen("sparse_fp16_field"); field.data_type = ZVEC_DATA_TYPE_SPARSE_VECTOR_FP16; uint16_t sparse_fp16_values[] = {0x3C00, 0x4000, 0xC000}; field.value.vector_value.data = (const float *)sparse_fp16_values; field.value.vector_value.length = sizeof(sparse_fp16_values) / sizeof(uint16_t); err = zvec_doc_add_field_by_struct(doc, &field); TEST_ASSERT(err == ZVEC_OK); // SPARSE_VECTOR_FP32 memset(&field, 0, sizeof(field)); field.name.data = "sparse_fp32_field"; field.name.length = strlen("sparse_fp32_field"); field.data_type = ZVEC_DATA_TYPE_SPARSE_VECTOR_FP32; float sparse_fp32_values[] = {1.5f, -2.5f, 3.5f}; field.value.vector_value.data = sparse_fp32_values; field.value.vector_value.length = sizeof(sparse_fp32_values) / sizeof(float); err = zvec_doc_add_field_by_struct(doc, &field); TEST_ASSERT(err == ZVEC_OK); // Array types // ARRAY_BINARY memset(&field, 0, sizeof(field)); field.name.data = "array_binary_field"; field.name.length = strlen("array_binary_field"); field.data_type = ZVEC_DATA_TYPE_ARRAY_BINARY; uint8_t array_bin_data[] = { 1, 0, 0, 0, 0x01, // length=1, data=0x01 2, 0, 0, 0, 0x02, 0x03, // length=2, data=0x02,0x03 2, 0, 0, 0, 0x04, 0x05 // length=2, data=0x04,0x05 }; field.value.binary_value.data = array_bin_data; field.value.binary_value.length = sizeof(array_bin_data); err = zvec_doc_add_field_by_struct(doc, &field); TEST_ASSERT(err == ZVEC_OK); // ARRAY_STRING memset(&field, 0, sizeof(field)); field.name.data = "array_string_field"; field.name.length = strlen("array_string_field"); field.data_type = ZVEC_DATA_TYPE_ARRAY_STRING; const char array_string_data[] = "str1\0str2\0str3\0"; field.value.string_value.data = (char *)array_string_data; field.value.string_value.length = sizeof(array_string_data); err = zvec_doc_add_field_by_struct(doc, &field); TEST_ASSERT(err == ZVEC_OK); // ARRAY_BOOL memset(&field, 0, sizeof(field)); field.name.data = "array_bool_field"; field.name.length = strlen("array_bool_field"); field.data_type = ZVEC_DATA_TYPE_ARRAY_BOOL; bool array_bool_data[] = {true, false, true, false}; field.value.binary_value.data = (const uint8_t *)array_bool_data; field.value.binary_value.length = sizeof(array_bool_data); err = zvec_doc_add_field_by_struct(doc, &field); TEST_ASSERT(err == ZVEC_OK); // ARRAY_INT32 memset(&field, 0, sizeof(field)); field.name.data = "array_int32_field"; field.name.length = strlen("array_int32_field"); field.data_type = ZVEC_DATA_TYPE_ARRAY_INT32; int32_t array_int32_data[] = {-100, -50, 0, 50, 100}; field.value.vector_value.data = (const float *)array_int32_data; field.value.vector_value.length = sizeof(array_int32_data) / sizeof(int32_t); err = zvec_doc_add_field_by_struct(doc, &field); TEST_ASSERT(err == ZVEC_OK); // ARRAY_INT64 memset(&field, 0, sizeof(field)); field.name.data = "array_int64_field"; field.name.length = strlen("array_int64_field"); field.data_type = ZVEC_DATA_TYPE_ARRAY_INT64; int64_t array_int64_data[] = {-1000000, -500000, 0, 500000, 1000000}; field.value.vector_value.data = (const float *)array_int64_data; field.value.vector_value.length = sizeof(array_int64_data) / sizeof(int64_t); err = zvec_doc_add_field_by_struct(doc, &field); TEST_ASSERT(err == ZVEC_OK); // ARRAY_UINT32 memset(&field, 0, sizeof(field)); field.name.data = "array_uint32_field"; field.name.length = strlen("array_uint32_field"); field.data_type = ZVEC_DATA_TYPE_ARRAY_UINT32; uint32_t array_uint32_data[] = {0, 100, 1000, 10000, 4294967295U}; field.value.vector_value.data = (const float *)array_uint32_data; field.value.vector_value.length = sizeof(array_uint32_data) / sizeof(uint32_t); err = zvec_doc_add_field_by_struct(doc, &field); TEST_ASSERT(err == ZVEC_OK); // ARRAY_UINT64 memset(&field, 0, sizeof(field)); field.name.data = "array_uint64_field"; field.name.length = strlen("array_uint64_field"); field.data_type = ZVEC_DATA_TYPE_ARRAY_UINT64; uint64_t array_uint64_data[] = {0, 100, 1000, 10000, 18446744073709551615ULL}; field.value.vector_value.data = (const float *)array_uint64_data; field.value.vector_value.length = sizeof(array_uint64_data) / sizeof(uint64_t); err = zvec_doc_add_field_by_struct(doc, &field); TEST_ASSERT(err == ZVEC_OK); // ARRAY_FLOAT memset(&field, 0, sizeof(field)); field.name.data = "array_float_field"; field.name.length = strlen("array_float_field"); field.data_type = ZVEC_DATA_TYPE_ARRAY_FLOAT; float array_float_data[] = {-1.5f, -0.5f, 0.0f, 0.5f, 1.5f}; field.value.vector_value.data = array_float_data; field.value.vector_value.length = sizeof(array_float_data) / sizeof(float); err = zvec_doc_add_field_by_struct(doc, &field); TEST_ASSERT(err == ZVEC_OK); // ARRAY_DOUBLE memset(&field, 0, sizeof(field)); field.name.data = "array_double_field"; field.name.length = strlen("array_double_field"); field.data_type = ZVEC_DATA_TYPE_ARRAY_DOUBLE; double array_double_data[] = {-1.1, -0.1, 0.0, 0.1, 1.1}; field.value.vector_value.data = (const float *)array_double_data; field.value.vector_value.length = sizeof(array_double_data) / sizeof(double); err = zvec_doc_add_field_by_struct(doc, &field); TEST_ASSERT(err == ZVEC_OK); // Verify we can retrieve some of the values void *result = NULL; size_t result_size = 0; err = zvec_doc_get_field_value_copy(doc, "int32_field", ZVEC_DATA_TYPE_INT32, &result, &result_size); TEST_ASSERT(err == ZVEC_OK && result_size == sizeof(int32_t)); if (result) { TEST_ASSERT(*(int32_t *)result == -12345); zvec_free(result); } err = zvec_doc_get_field_value_copy(doc, "float_field", ZVEC_DATA_TYPE_FLOAT, &result, &result_size); TEST_ASSERT(err == ZVEC_OK && result_size == sizeof(float)); if (result) { TEST_ASSERT(fabs(*(float *)result - 3.14159f) < 0.0001f); zvec_free(result); } zvec_doc_destroy(doc); TEST_END(); } void test_doc_basic_operations(void); void test_doc_null_field_api(void); void test_doc_get_field_value_basic(void); void test_doc_get_field_value_copy(void); void test_doc_get_field_value_pointer(void); void test_doc_field_operations(void); void test_doc_error_conditions(void); void test_doc_serialization(void); void test_doc_add_field_by_value(void); void test_doc_add_field_by_struct(void); void test_doc_functions(void) { test_doc_basic_operations(); test_doc_null_field_api(); test_doc_get_field_value_basic(); test_doc_get_field_value_copy(); test_doc_get_field_value_pointer(); test_doc_field_operations(); test_doc_error_conditions(); test_doc_serialization(); } void test_doc_basic_operations(void) { TEST_START(); // Create test document zvec_doc_t *doc = zvec_doc_create(); TEST_ASSERT(doc != NULL); // Test primary key operations zvec_doc_set_pk(doc, "test_doc_complete"); const char *pk = zvec_doc_get_pk_pointer(doc); TEST_ASSERT(pk != NULL); TEST_ASSERT(strcmp(pk, "test_doc_complete") == 0); // Test document ID and score operations zvec_doc_set_doc_id(doc, 99999); uint64_t doc_id = zvec_doc_get_doc_id(doc); TEST_ASSERT(doc_id == 99999); zvec_doc_set_score(doc, 0.95f); float score = zvec_doc_get_score(doc); TEST_ASSERT(score == 0.95f); // Test operator operations zvec_doc_set_operator(doc, ZVEC_DOC_OP_INSERT); zvec_doc_operator_t op = zvec_doc_get_operator(doc); TEST_ASSERT(op == ZVEC_DOC_OP_INSERT); zvec_doc_destroy(doc); TEST_END(); } void test_doc_null_field_api(void) { TEST_START(); zvec_doc_t *doc = zvec_doc_create(); TEST_ASSERT(doc != NULL); if (!doc) { TEST_END(); return; } zvec_error_code_t err = zvec_doc_set_field_null(doc, "nullable_field"); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(zvec_doc_has_field(doc, "nullable_field") == true); TEST_ASSERT(zvec_doc_has_field_value(doc, "nullable_field") == false); TEST_ASSERT(zvec_doc_is_field_null(doc, "nullable_field") == true); err = zvec_doc_set_field_null(NULL, "nullable_field"); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); err = zvec_doc_set_field_null(doc, NULL); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); zvec_doc_destroy(doc); TEST_END(); } void test_doc_get_field_value_basic(void) { TEST_START(); zvec_doc_t *doc = zvec_doc_create(); TEST_ASSERT(doc != NULL); zvec_error_code_t err; printf( "=== Testing zvec_doc_get_field_value_basic with all supported types " "===\n"); // BOOL type zvec_doc_field_t bool_field; bool_field.name.data = "bool_field"; bool_field.name.length = strlen("bool_field"); bool_field.data_type = ZVEC_DATA_TYPE_BOOL; bool_field.value.bool_value = true; err = zvec_doc_add_field_by_struct(doc, &bool_field); TEST_ASSERT(err == ZVEC_OK); bool bool_result; err = zvec_doc_get_field_value_basic(doc, "bool_field", ZVEC_DATA_TYPE_BOOL, &bool_result, sizeof(bool_result)); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(bool_result == true); // INT32 type zvec_doc_field_t int32_field; int32_field.name.data = "int32_field"; int32_field.name.length = strlen("int32_field"); int32_field.data_type = ZVEC_DATA_TYPE_INT32; int32_field.value.int32_value = -2147483648; // Min int32 err = zvec_doc_add_field_by_struct(doc, &int32_field); TEST_ASSERT(err == ZVEC_OK); int32_t int32_result; err = zvec_doc_get_field_value_basic(doc, "int32_field", ZVEC_DATA_TYPE_INT32, &int32_result, sizeof(int32_result)); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(int32_result == -2147483648); // INT64 type zvec_doc_field_t int64_field; int64_field.name.data = "int64_field"; int64_field.name.length = strlen("int64_field"); int64_field.data_type = ZVEC_DATA_TYPE_INT64; int64_field.value.int64_value = 9223372036854775807LL; // Max int64 err = zvec_doc_add_field_by_struct(doc, &int64_field); TEST_ASSERT(err == ZVEC_OK); int64_t int64_result; err = zvec_doc_get_field_value_basic(doc, "int64_field", ZVEC_DATA_TYPE_INT64, &int64_result, sizeof(int64_result)); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(int64_result == 9223372036854775807LL); // UINT32 type zvec_doc_field_t uint32_field; uint32_field.name.data = "uint32_field"; uint32_field.name.length = strlen("uint32_field"); uint32_field.data_type = ZVEC_DATA_TYPE_UINT32; uint32_field.value.uint32_value = 4294967295U; // Max uint32 err = zvec_doc_add_field_by_struct(doc, &uint32_field); TEST_ASSERT(err == ZVEC_OK); uint32_t uint32_result; err = zvec_doc_get_field_value_basic(doc, "uint32_field", ZVEC_DATA_TYPE_UINT32, &uint32_result, sizeof(uint32_result)); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(uint32_result == 4294967295U); // UINT64 type zvec_doc_field_t uint64_field; uint64_field.name.data = "uint64_field"; uint64_field.name.length = strlen("uint64_field"); uint64_field.data_type = ZVEC_DATA_TYPE_UINT64; uint64_field.value.uint64_value = 18446744073709551615ULL; // Max uint64 err = zvec_doc_add_field_by_struct(doc, &uint64_field); TEST_ASSERT(err == ZVEC_OK); uint64_t uint64_result; err = zvec_doc_get_field_value_basic(doc, "uint64_field", ZVEC_DATA_TYPE_UINT64, &uint64_result, sizeof(uint64_result)); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(uint64_result == 18446744073709551615ULL); // FLOAT type zvec_doc_field_t float_field; float_field.name.data = "float_field"; float_field.name.length = strlen("float_field"); float_field.data_type = ZVEC_DATA_TYPE_FLOAT; float_field.value.float_value = 3.14159265359f; err = zvec_doc_add_field_by_struct(doc, &float_field); TEST_ASSERT(err == ZVEC_OK); float float_result; err = zvec_doc_get_field_value_basic(doc, "float_field", ZVEC_DATA_TYPE_FLOAT, &float_result, sizeof(float_result)); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(fabsf(float_result - 3.14159265359f) < 1e-6f); // DOUBLE type zvec_doc_field_t double_field; double_field.name.data = "double_field"; double_field.name.length = strlen("double_field"); double_field.data_type = ZVEC_DATA_TYPE_DOUBLE; double_field.value.double_value = 2.71828182845904523536; err = zvec_doc_add_field_by_struct(doc, &double_field); TEST_ASSERT(err == ZVEC_OK); double double_result; err = zvec_doc_get_field_value_basic(doc, "double_field", ZVEC_DATA_TYPE_DOUBLE, &double_result, sizeof(double_result)); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(fabs(double_result - 2.71828182845904523536) < 1e-15); zvec_doc_destroy(doc); TEST_END(); } void test_doc_get_field_value_copy(void) { TEST_START(); zvec_doc_t *doc = zvec_doc_create(); TEST_ASSERT(doc != NULL); zvec_error_code_t err; printf( "=== Testing zvec_doc_get_field_value_copy with all supported types " "===\n"); // Basic scalar types first bool bool_val = true; err = zvec_doc_add_field_by_value(doc, "bool_field2", ZVEC_DATA_TYPE_BOOL, &bool_val, sizeof(bool_val)); TEST_ASSERT(err == ZVEC_OK); void *bool_copy_result; size_t bool_copy_size; err = zvec_doc_get_field_value_copy(doc, "bool_field2", ZVEC_DATA_TYPE_BOOL, &bool_copy_result, &bool_copy_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(bool_copy_result != NULL); TEST_ASSERT(bool_copy_size == sizeof(bool)); TEST_ASSERT(*(bool *)bool_copy_result == true); zvec_free(bool_copy_result); int32_t int32_val = -12345; err = zvec_doc_add_field_by_value(doc, "int32_field2", ZVEC_DATA_TYPE_INT32, &int32_val, sizeof(int32_val)); TEST_ASSERT(err == ZVEC_OK); void *int32_copy_result; size_t int32_copy_size; err = zvec_doc_get_field_value_copy(doc, "int32_field2", ZVEC_DATA_TYPE_INT32, &int32_copy_result, &int32_copy_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(int32_copy_result != NULL); TEST_ASSERT(int32_copy_size == sizeof(int32_t)); TEST_ASSERT(*(int32_t *)int32_copy_result == -12345); zvec_free(int32_copy_result); int64_t int64_val = -9223372036854775807LL; err = zvec_doc_add_field_by_value(doc, "int64_field2", ZVEC_DATA_TYPE_INT64, &int64_val, sizeof(int64_val)); TEST_ASSERT(err == ZVEC_OK); void *int64_copy_result; size_t int64_copy_size; err = zvec_doc_get_field_value_copy(doc, "int64_field2", ZVEC_DATA_TYPE_INT64, &int64_copy_result, &int64_copy_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(int64_copy_result != NULL); TEST_ASSERT(int64_copy_size == sizeof(int64_t)); TEST_ASSERT(*(int64_t *)int64_copy_result == -9223372036854775807LL); zvec_free(int64_copy_result); uint32_t uint32_val = 4000000000U; err = zvec_doc_add_field_by_value(doc, "uint32_field2", ZVEC_DATA_TYPE_UINT32, &uint32_val, sizeof(uint32_val)); TEST_ASSERT(err == ZVEC_OK); void *uint32_copy_result; size_t uint32_copy_size; err = zvec_doc_get_field_value_copy(doc, "uint32_field2", ZVEC_DATA_TYPE_UINT32, &uint32_copy_result, &uint32_copy_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(uint32_copy_result != NULL); TEST_ASSERT(uint32_copy_size == sizeof(uint32_t)); TEST_ASSERT(*(uint32_t *)uint32_copy_result == 4000000000U); zvec_free(uint32_copy_result); uint64_t uint64_val = 18000000000000000000ULL; err = zvec_doc_add_field_by_value(doc, "uint64_field2", ZVEC_DATA_TYPE_UINT64, &uint64_val, sizeof(uint64_val)); TEST_ASSERT(err == ZVEC_OK); void *uint64_copy_result; size_t uint64_copy_size; err = zvec_doc_get_field_value_copy(doc, "uint64_field2", ZVEC_DATA_TYPE_UINT64, &uint64_copy_result, &uint64_copy_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(uint64_copy_result != NULL); TEST_ASSERT(uint64_copy_size == sizeof(uint64_t)); TEST_ASSERT(*(uint64_t *)uint64_copy_result == 18000000000000000000ULL); zvec_free(uint64_copy_result); float float_val = 3.14159265f; err = zvec_doc_add_field_by_value(doc, "float_field2", ZVEC_DATA_TYPE_FLOAT, &float_val, sizeof(float_val)); TEST_ASSERT(err == ZVEC_OK); void *float_copy_result; size_t float_copy_size; err = zvec_doc_get_field_value_copy(doc, "float_field2", ZVEC_DATA_TYPE_FLOAT, &float_copy_result, &float_copy_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(float_copy_result != NULL); TEST_ASSERT(float_copy_size == sizeof(float)); TEST_ASSERT(fabs(*(float *)float_copy_result - 3.14159265f) < 1e-6f); zvec_free(float_copy_result); double double_val = 2.718281828459045; err = zvec_doc_add_field_by_value(doc, "double_field2", ZVEC_DATA_TYPE_DOUBLE, &double_val, sizeof(double_val)); TEST_ASSERT(err == ZVEC_OK); void *double_copy_result; size_t double_copy_size; err = zvec_doc_get_field_value_copy(doc, "double_field2", ZVEC_DATA_TYPE_DOUBLE, &double_copy_result, &double_copy_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(double_copy_result != NULL); TEST_ASSERT(double_copy_size == sizeof(double)); TEST_ASSERT(fabs(*(double *)double_copy_result - 2.718281828459045) < 1e-15); zvec_free(double_copy_result); // String and binary types zvec_doc_field_t string_field; string_field.name.data = "string_field"; string_field.name.length = strlen("string_field"); string_field.data_type = ZVEC_DATA_TYPE_STRING; string_field.value.string_value = *zvec_string_create("Hello, 世界!"); err = zvec_doc_add_field_by_struct(doc, &string_field); TEST_ASSERT(err == ZVEC_OK); void *string_result; size_t string_size; err = zvec_doc_get_field_value_copy( doc, "string_field", ZVEC_DATA_TYPE_STRING, &string_result, &string_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(string_result != NULL); TEST_ASSERT(string_size == strlen("Hello, 世界!")); TEST_ASSERT(memcmp(string_result, "Hello, 世界!", string_size) == 0); zvec_free(string_result); zvec_doc_field_t binary_field; binary_field.name.data = "binary_field"; binary_field.name.length = strlen("binary_field"); binary_field.data_type = ZVEC_DATA_TYPE_BINARY; uint8_t binary_data[] = {0x00, 0x01, 0x02, 0xFF, 0xFE, 0xFD}; binary_field.value.string_value = *zvec_bin_create(binary_data, sizeof(binary_data)); err = zvec_doc_add_field_by_struct(doc, &binary_field); TEST_ASSERT(err == ZVEC_OK); void *binary_result; size_t binary_size; err = zvec_doc_get_field_value_copy( doc, "binary_field", ZVEC_DATA_TYPE_BINARY, &binary_result, &binary_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(binary_result != NULL); TEST_ASSERT(binary_size == 6); TEST_ASSERT(memcmp(binary_result, "\x00\x01\x02\xFF\xFE\xFD", binary_size) == 0); zvec_free(binary_result); // VECTOR_FP32 type float test_vector[] = {1.1f, 2.2f, 3.3f, 4.4f, 5.5f}; zvec_doc_field_t fp32_vec_field; fp32_vec_field.name.data = "fp32_vec_field"; fp32_vec_field.name.length = strlen("fp32_vec_field"); fp32_vec_field.data_type = ZVEC_DATA_TYPE_VECTOR_FP32; fp32_vec_field.value.vector_value.data = test_vector; fp32_vec_field.value.vector_value.length = 5; err = zvec_doc_add_field_by_struct(doc, &fp32_vec_field); TEST_ASSERT(err == ZVEC_OK); void *fp32_vec_result; size_t fp32_vec_size; err = zvec_doc_get_field_value_copy(doc, "fp32_vec_field", ZVEC_DATA_TYPE_VECTOR_FP32, &fp32_vec_result, &fp32_vec_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(fp32_vec_result != NULL); TEST_ASSERT(fp32_vec_size == 5 * sizeof(float)); TEST_ASSERT(memcmp(fp32_vec_result, test_vector, fp32_vec_size) == 0); zvec_free(fp32_vec_result); // VECTOR_FP16 type (16-bit float vector) uint16_t fp16_data[] = {0x3C00, 0x4000, 0x4200, 0x4400}; // FP16: 1.0, 2.0, 3.0, 4.0 err = zvec_doc_add_field_by_value(doc, "fp16_vec_field", ZVEC_DATA_TYPE_VECTOR_FP16, fp16_data, sizeof(fp16_data)); TEST_ASSERT(err == ZVEC_OK); void *fp16_result; size_t fp16_size; err = zvec_doc_get_field_value_copy(doc, "fp16_vec_field", ZVEC_DATA_TYPE_VECTOR_FP16, &fp16_result, &fp16_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(fp16_result != NULL); TEST_ASSERT(fp16_size == sizeof(fp16_data)); TEST_ASSERT(memcmp(fp16_result, fp16_data, fp16_size) == 0); zvec_free(fp16_result); // VECTOR_INT8 type int8_t int8_data[] = {-128, -1, 0, 1, 127}; err = zvec_doc_add_field_by_value(doc, "int8_vec_field", ZVEC_DATA_TYPE_VECTOR_INT8, int8_data, sizeof(int8_data)); TEST_ASSERT(err == ZVEC_OK); void *int8_result; size_t int8_size; err = zvec_doc_get_field_value_copy(doc, "int8_vec_field", ZVEC_DATA_TYPE_VECTOR_INT8, &int8_result, &int8_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(int8_result != NULL); TEST_ASSERT(int8_size == sizeof(int8_data)); TEST_ASSERT(memcmp(int8_result, int8_data, int8_size) == 0); zvec_free(int8_result); // VECTOR_BINARY32 type (32-bit aligned binary vector) uint8_t bin32_data[] = {0xAA, 0x55, 0xAA, 0x55}; err = zvec_doc_add_field_by_value(doc, "bin32_vec_field", ZVEC_DATA_TYPE_VECTOR_BINARY32, bin32_data, sizeof(bin32_data)); TEST_ASSERT(err == ZVEC_OK); void *bin32_result; size_t bin32_size; err = zvec_doc_get_field_value_copy(doc, "bin32_vec_field", ZVEC_DATA_TYPE_VECTOR_BINARY32, &bin32_result, &bin32_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(bin32_result != NULL); TEST_ASSERT(bin32_size == sizeof(bin32_data)); TEST_ASSERT(memcmp(bin32_result, bin32_data, bin32_size) == 0); zvec_free(bin32_result); // VECTOR_BINARY64 type (64-bit aligned binary vector) uint64_t bin64_data[] = {0xAA55AA55AA55AA55ULL, 0x55AA55AA55AA55AAULL}; err = zvec_doc_add_field_by_value(doc, "bin64_vec_field", ZVEC_DATA_TYPE_VECTOR_BINARY64, bin64_data, sizeof(bin64_data)); TEST_ASSERT(err == ZVEC_OK); void *bin64_result; size_t bin64_size; err = zvec_doc_get_field_value_copy(doc, "bin64_vec_field", ZVEC_DATA_TYPE_VECTOR_BINARY64, &bin64_result, &bin64_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(bin64_result != NULL); TEST_ASSERT(bin64_size == sizeof(bin64_data)); TEST_ASSERT(memcmp(bin64_result, bin64_data, bin64_size) == 0); zvec_free(bin64_result); // VECTOR_FP64 type (double precision vector) double fp64_data[] = {1.1, 2.2, 3.3, 4.4}; err = zvec_doc_add_field_by_value(doc, "fp64_vec_field", ZVEC_DATA_TYPE_VECTOR_FP64, fp64_data, sizeof(fp64_data)); TEST_ASSERT(err == ZVEC_OK); void *fp64_result; size_t fp64_size; err = zvec_doc_get_field_value_copy(doc, "fp64_vec_field", ZVEC_DATA_TYPE_VECTOR_FP64, &fp64_result, &fp64_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(fp64_result != NULL); TEST_ASSERT(fp64_size == sizeof(fp64_data)); TEST_ASSERT(memcmp(fp64_result, fp64_data, fp64_size) == 0); zvec_free(fp64_result); // VECTOR_INT16 type int16_t int16_data[] = {-32768, -1, 0, 1, 32767}; err = zvec_doc_add_field_by_value(doc, "int16_vec_field", ZVEC_DATA_TYPE_VECTOR_INT16, int16_data, sizeof(int16_data)); TEST_ASSERT(err == ZVEC_OK); void *int16_result; size_t int16_size; err = zvec_doc_get_field_value_copy(doc, "int16_vec_field", ZVEC_DATA_TYPE_VECTOR_INT16, &int16_result, &int16_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(int16_result != NULL); TEST_ASSERT(int16_size == sizeof(int16_data)); TEST_ASSERT(memcmp(int16_result, int16_data, int16_size) == 0); zvec_free(int16_result); // SPARSE_VECTOR_FP16 type - format: [nnz(uint32_t)][indices...][values...] uint32_t sparse_fp16_nnz = 3; size_t sparse_fp16_size_input = sizeof(uint32_t) + sparse_fp16_nnz * (sizeof(uint32_t) + sizeof(uint16_t)); void *sparse_fp16_input = malloc(sparse_fp16_size_input); uint32_t *fp16_nnz_ptr = (uint32_t *)sparse_fp16_input; *fp16_nnz_ptr = sparse_fp16_nnz; uint32_t *fp16_indices = (uint32_t *)((char *)sparse_fp16_input + sizeof(uint32_t)); uint16_t *fp16_values = (uint16_t *)((char *)sparse_fp16_input + sizeof(uint32_t) + sparse_fp16_nnz * sizeof(uint32_t)); fp16_indices[0] = 0; fp16_indices[1] = 5; fp16_indices[2] = 10; fp16_values[0] = 0x3C00; fp16_values[1] = 0x4000; fp16_values[2] = 0x4200; // FP16: 1.0, 2.0, 3.0 err = zvec_doc_add_field_by_value(doc, "sparse_fp16_field", ZVEC_DATA_TYPE_SPARSE_VECTOR_FP16, sparse_fp16_input, sparse_fp16_size_input); TEST_ASSERT(err == ZVEC_OK); free(sparse_fp16_input); void *sparse_fp16_result; size_t sparse_fp16_result_size; err = zvec_doc_get_field_value_copy( doc, "sparse_fp16_field", ZVEC_DATA_TYPE_SPARSE_VECTOR_FP16, &sparse_fp16_result, &sparse_fp16_result_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(sparse_fp16_result != NULL); // Sparse vector format: [nnz(size_t)][indices...][values...] size_t retrieved_nnz = *(size_t *)sparse_fp16_result; TEST_ASSERT(retrieved_nnz == 3); uint32_t *retrieved_fp16_indices = (uint32_t *)((char *)sparse_fp16_result + sizeof(size_t)); uint16_t *retrieved_fp16_vals = (uint16_t *)((char *)sparse_fp16_result + sizeof(size_t) + retrieved_nnz * sizeof(uint32_t)); TEST_ASSERT(retrieved_fp16_indices[0] == 0); TEST_ASSERT(retrieved_fp16_indices[1] == 5); TEST_ASSERT(retrieved_fp16_indices[2] == 10); TEST_ASSERT(retrieved_fp16_vals[0] == 0x3C00); TEST_ASSERT(retrieved_fp16_vals[1] == 0x4000); TEST_ASSERT(retrieved_fp16_vals[2] == 0x4200); zvec_free(sparse_fp16_result); // SPARSE_VECTOR_FP32 type - format: [nnz(uint32_t)][indices...][values...] uint32_t sparse_fp32_nnz = 4; size_t sparse_fp32_size_input = sizeof(uint32_t) + sparse_fp32_nnz * (sizeof(uint32_t) + sizeof(float)); void *sparse_fp32_input = malloc(sparse_fp32_size_input); uint32_t *fp32_nnz_ptr = (uint32_t *)sparse_fp32_input; *fp32_nnz_ptr = sparse_fp32_nnz; uint32_t *fp32_indices = (uint32_t *)((char *)sparse_fp32_input + sizeof(uint32_t)); float *fp32_values = (float *)((char *)sparse_fp32_input + sizeof(uint32_t) + sparse_fp32_nnz * sizeof(uint32_t)); fp32_indices[0] = 2; fp32_indices[1] = 7; fp32_indices[2] = 15; fp32_indices[3] = 20; fp32_values[0] = 1.5f; fp32_values[1] = 2.5f; fp32_values[2] = 3.5f; fp32_values[3] = 4.5f; err = zvec_doc_add_field_by_value(doc, "sparse_fp32_field", ZVEC_DATA_TYPE_SPARSE_VECTOR_FP32, sparse_fp32_input, sparse_fp32_size_input); TEST_ASSERT(err == ZVEC_OK); free(sparse_fp32_input); void *sparse_fp32_result; size_t sparse_fp32_result_size; err = zvec_doc_get_field_value_copy( doc, "sparse_fp32_field", ZVEC_DATA_TYPE_SPARSE_VECTOR_FP32, &sparse_fp32_result, &sparse_fp32_result_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(sparse_fp32_result != NULL); retrieved_nnz = *(size_t *)sparse_fp32_result; TEST_ASSERT(retrieved_nnz == 4); uint32_t *retrieved_fp32_indices = (uint32_t *)((char *)sparse_fp32_result + sizeof(size_t)); float *retrieved_fp32_vals = (float *)((char *)sparse_fp32_result + sizeof(size_t) + retrieved_nnz * sizeof(uint32_t)); TEST_ASSERT(retrieved_fp32_indices[0] == 2); TEST_ASSERT(retrieved_fp32_indices[1] == 7); TEST_ASSERT(retrieved_fp32_indices[2] == 15); TEST_ASSERT(retrieved_fp32_indices[3] == 20); TEST_ASSERT(fabs(retrieved_fp32_vals[0] - 1.5f) < 1e-5f); TEST_ASSERT(fabs(retrieved_fp32_vals[1] - 2.5f) < 1e-5f); TEST_ASSERT(fabs(retrieved_fp32_vals[2] - 3.5f) < 1e-5f); TEST_ASSERT(fabs(retrieved_fp32_vals[3] - 4.5f) < 1e-5f); zvec_free(sparse_fp32_result); // ARRAY_BINARY type // Format: [length(uint32_t)][data][length][data]... uint8_t array_bin_data[] = { 1, 0, 0, 0, 0x01, // length=1, data=0x01 2, 0, 0, 0, 0x02, 0x03, // length=2, data=0x02,0x03 2, 0, 0, 0, 0x04, 0x05 // length=2, data=0x04,0x05 }; err = zvec_doc_add_field_by_value(doc, "array_binary_field", ZVEC_DATA_TYPE_ARRAY_BINARY, array_bin_data, sizeof(array_bin_data)); TEST_ASSERT(err == ZVEC_OK); void *array_binary_result; size_t array_binary_size; err = zvec_doc_get_field_value_copy(doc, "array_binary_field", ZVEC_DATA_TYPE_ARRAY_BINARY, &array_binary_result, &array_binary_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(array_binary_result != NULL); // The result is a contiguous buffer of binary data without length prefixes TEST_ASSERT(array_binary_size == 5); // 1 + 2 + 2 bytes const uint8_t *result_bytes = (const uint8_t *)array_binary_result; TEST_ASSERT(result_bytes[0] == 0x01); TEST_ASSERT(result_bytes[1] == 0x02); TEST_ASSERT(result_bytes[2] == 0x03); TEST_ASSERT(result_bytes[3] == 0x04); TEST_ASSERT(result_bytes[4] == 0x05); zvec_free(array_binary_result); // ARRAY_STRING type const char *array_str_data[] = {"str1", "str2", "str3"}; zvec_string_t *array_zvec_str[3]; for (int i = 0; i < 3; i++) { array_zvec_str[i] = zvec_string_create(array_str_data[i]); } err = zvec_doc_add_field_by_value(doc, "array_string_field", ZVEC_DATA_TYPE_ARRAY_STRING, array_zvec_str, sizeof(array_zvec_str)); TEST_ASSERT(err == ZVEC_OK); void *array_string_result; size_t array_string_size; err = zvec_doc_get_field_value_copy(doc, "array_string_field", ZVEC_DATA_TYPE_ARRAY_STRING, &array_string_result, &array_string_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(array_string_result != NULL); zvec_free(array_string_result); for (int i = 0; i < 3; i++) { zvec_free_string(array_zvec_str[i]); } free(string_field.value.string_value.data); // ARRAY_BOOL type bool array_bool_data[] = {true, false, true, false, true}; err = zvec_doc_add_field_by_value(doc, "array_bool_field", ZVEC_DATA_TYPE_ARRAY_BOOL, array_bool_data, sizeof(array_bool_data)); TEST_ASSERT(err == ZVEC_OK); void *array_bool_result; size_t array_bool_size; err = zvec_doc_get_field_value_copy(doc, "array_bool_field", ZVEC_DATA_TYPE_ARRAY_BOOL, &array_bool_result, &array_bool_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(array_bool_result != NULL); // Verify the bit-packed bool array uint8_t *bool_bytes = (uint8_t *)array_bool_result; TEST_ASSERT((bool_bytes[0] & 0x01) != 0); // index 0: true TEST_ASSERT((bool_bytes[0] & 0x02) == 0); // index 1: false TEST_ASSERT((bool_bytes[0] & 0x04) != 0); // index 2: true TEST_ASSERT((bool_bytes[0] & 0x08) == 0); // index 3: false TEST_ASSERT((bool_bytes[0] & 0x10) != 0); // index 4: true zvec_free(array_bool_result); // ARRAY_INT32 type int32_t array_int32_data[] = {100, 200, 300}; err = zvec_doc_add_field_by_value(doc, "array_int32_field", ZVEC_DATA_TYPE_ARRAY_INT32, array_int32_data, sizeof(array_int32_data)); TEST_ASSERT(err == ZVEC_OK); void *array_int32_result; size_t array_int32_size; err = zvec_doc_get_field_value_copy(doc, "array_int32_field", ZVEC_DATA_TYPE_ARRAY_INT32, &array_int32_result, &array_int32_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(array_int32_result != NULL); TEST_ASSERT(array_int32_size == sizeof(array_int32_data)); TEST_ASSERT(((int32_t *)array_int32_result)[0] == 100); TEST_ASSERT(((int32_t *)array_int32_result)[1] == 200); TEST_ASSERT(((int32_t *)array_int32_result)[2] == 300); zvec_free(array_int32_result); // ARRAY_INT64 type int64_t array_int64_data[] = {-9223372036854775807LL, 0, 9223372036854775807LL}; err = zvec_doc_add_field_by_value(doc, "array_int64_field", ZVEC_DATA_TYPE_ARRAY_INT64, array_int64_data, sizeof(array_int64_data)); TEST_ASSERT(err == ZVEC_OK); void *array_int64_result; size_t array_int64_size; err = zvec_doc_get_field_value_copy(doc, "array_int64_field", ZVEC_DATA_TYPE_ARRAY_INT64, &array_int64_result, &array_int64_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(array_int64_result != NULL); TEST_ASSERT(array_int64_size == sizeof(array_int64_data)); TEST_ASSERT(((int64_t *)array_int64_result)[0] == -9223372036854775807LL); TEST_ASSERT(((int64_t *)array_int64_result)[1] == 0); TEST_ASSERT(((int64_t *)array_int64_result)[2] == 9223372036854775807LL); zvec_free(array_int64_result); // ARRAY_UINT32 type uint32_t array_uint32_data[] = {0U, 1000000U, 4000000000U}; err = zvec_doc_add_field_by_value( doc, "array_uint32_field", ZVEC_DATA_TYPE_ARRAY_UINT32, array_uint32_data, sizeof(array_uint32_data)); TEST_ASSERT(err == ZVEC_OK); void *array_uint32_result; size_t array_uint32_size; err = zvec_doc_get_field_value_copy(doc, "array_uint32_field", ZVEC_DATA_TYPE_ARRAY_UINT32, &array_uint32_result, &array_uint32_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(array_uint32_result != NULL); TEST_ASSERT(array_uint32_size == sizeof(array_uint32_data)); TEST_ASSERT(((uint32_t *)array_uint32_result)[0] == 0U); TEST_ASSERT(((uint32_t *)array_uint32_result)[1] == 1000000U); TEST_ASSERT(((uint32_t *)array_uint32_result)[2] == 4000000000U); zvec_free(array_uint32_result); // ARRAY_UINT64 type uint64_t array_uint64_data[] = {0ULL, 1000000000000ULL, 18000000000000000000ULL}; err = zvec_doc_add_field_by_value( doc, "array_uint64_field", ZVEC_DATA_TYPE_ARRAY_UINT64, array_uint64_data, sizeof(array_uint64_data)); TEST_ASSERT(err == ZVEC_OK); void *array_uint64_result; size_t array_uint64_size; err = zvec_doc_get_field_value_copy(doc, "array_uint64_field", ZVEC_DATA_TYPE_ARRAY_UINT64, &array_uint64_result, &array_uint64_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(array_uint64_result != NULL); TEST_ASSERT(array_uint64_size == sizeof(array_uint64_data)); TEST_ASSERT(((uint64_t *)array_uint64_result)[0] == 0ULL); TEST_ASSERT(((uint64_t *)array_uint64_result)[1] == 1000000000000ULL); TEST_ASSERT(((uint64_t *)array_uint64_result)[2] == 18000000000000000000ULL); zvec_free(array_uint64_result); // ARRAY_FLOAT type float array_float_data[] = {1.5f, 2.5f, 3.5f}; err = zvec_doc_add_field_by_value(doc, "array_float_field", ZVEC_DATA_TYPE_ARRAY_FLOAT, array_float_data, sizeof(array_float_data)); TEST_ASSERT(err == ZVEC_OK); void *array_float_result; size_t array_float_size; err = zvec_doc_get_field_value_copy(doc, "array_float_field", ZVEC_DATA_TYPE_ARRAY_FLOAT, &array_float_result, &array_float_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(array_float_result != NULL); TEST_ASSERT(array_float_size == sizeof(array_float_data)); TEST_ASSERT(((float *)array_float_result)[0] == 1.5f); TEST_ASSERT(((float *)array_float_result)[1] == 2.5f); TEST_ASSERT(((float *)array_float_result)[2] == 3.5f); zvec_free(array_float_result); // ARRAY_DOUBLE type double array_double_data[] = {1.111111, 2.222222, 3.333333}; err = zvec_doc_add_field_by_value( doc, "array_double_field", ZVEC_DATA_TYPE_ARRAY_DOUBLE, array_double_data, sizeof(array_double_data)); TEST_ASSERT(err == ZVEC_OK); void *array_double_result; size_t array_double_size; err = zvec_doc_get_field_value_copy(doc, "array_double_field", ZVEC_DATA_TYPE_ARRAY_DOUBLE, &array_double_result, &array_double_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(array_double_result != NULL); TEST_ASSERT(array_double_size == sizeof(array_double_data)); TEST_ASSERT(fabs(((double *)array_double_result)[0] - 1.111111) < 1e-10); TEST_ASSERT(fabs(((double *)array_double_result)[1] - 2.222222) < 1e-10); TEST_ASSERT(fabs(((double *)array_double_result)[2] - 3.333333) < 1e-10); zvec_free(array_double_result); zvec_free(binary_field.value.string_value.data); zvec_doc_destroy(doc); TEST_END(); } void test_doc_get_field_value_pointer(void) { TEST_START(); zvec_doc_t *doc = zvec_doc_create(); TEST_ASSERT(doc != NULL); zvec_error_code_t err; // Add fields for pointer testing zvec_doc_field_t bool_field; bool_field.name.data = "bool_field"; bool_field.name.length = strlen("bool_field"); bool_field.data_type = ZVEC_DATA_TYPE_BOOL; bool_field.value.bool_value = true; err = zvec_doc_add_field_by_struct(doc, &bool_field); TEST_ASSERT(err == ZVEC_OK); zvec_doc_field_t int32_field; int32_field.name.data = "int32_field"; int32_field.name.length = strlen("int32_field"); int32_field.data_type = ZVEC_DATA_TYPE_INT32; int32_field.value.int32_value = -2147483648; err = zvec_doc_add_field_by_struct(doc, &int32_field); TEST_ASSERT(err == ZVEC_OK); zvec_doc_field_t string_field; string_field.name.data = "string_field"; string_field.name.length = strlen("string_field"); string_field.data_type = ZVEC_DATA_TYPE_STRING; string_field.value.string_value = *zvec_string_create("Hello, 世界!"); err = zvec_doc_add_field_by_struct(doc, &string_field); TEST_ASSERT(err == ZVEC_OK); zvec_doc_field_t binary_field; binary_field.name.data = "binary_field"; binary_field.name.length = strlen("binary_field"); binary_field.data_type = ZVEC_DATA_TYPE_BINARY; uint8_t binary_data[] = {0x00, 0x01, 0x02, 0xFF, 0xFE, 0xFD}; binary_field.value.string_value = *zvec_bin_create(binary_data, sizeof(binary_data)); err = zvec_doc_add_field_by_struct(doc, &binary_field); TEST_ASSERT(err == ZVEC_OK); float test_vector[] = {1.1f, 2.2f, 3.3f, 4.4f, 5.5f}; zvec_doc_field_t fp32_vec_field; fp32_vec_field.name.data = "fp32_vec_field"; fp32_vec_field.name.length = strlen("fp32_vec_field"); fp32_vec_field.data_type = ZVEC_DATA_TYPE_VECTOR_FP32; fp32_vec_field.value.vector_value.data = test_vector; fp32_vec_field.value.vector_value.length = 5; err = zvec_doc_add_field_by_struct(doc, &fp32_vec_field); TEST_ASSERT(err == ZVEC_OK); // Add more fields for comprehensive pointer testing int64_t int64_val = -9223372036854775807LL; err = zvec_doc_add_field_by_value(doc, "int64_field_ptr", ZVEC_DATA_TYPE_INT64, &int64_val, sizeof(int64_val)); TEST_ASSERT(err == ZVEC_OK); uint32_t uint32_val = 4000000000U; err = zvec_doc_add_field_by_value(doc, "uint32_field_ptr", ZVEC_DATA_TYPE_UINT32, &uint32_val, sizeof(uint32_val)); TEST_ASSERT(err == ZVEC_OK); uint64_t uint64_val = 18000000000000000000ULL; err = zvec_doc_add_field_by_value(doc, "uint64_field_ptr", ZVEC_DATA_TYPE_UINT64, &uint64_val, sizeof(uint64_val)); TEST_ASSERT(err == ZVEC_OK); float float_val = 3.14159265f; err = zvec_doc_add_field_by_value(doc, "float_field_ptr", ZVEC_DATA_TYPE_FLOAT, &float_val, sizeof(float_val)); TEST_ASSERT(err == ZVEC_OK); double double_val = 2.718281828459045; err = zvec_doc_add_field_by_value(doc, "double_field_ptr", ZVEC_DATA_TYPE_DOUBLE, &double_val, sizeof(double_val)); TEST_ASSERT(err == ZVEC_OK); // VECTOR_BINARY64 uint64_t bin64_vec_data[] = {0xAA55AA55AA55AA55ULL, 0x55AA55AA55AA55AAULL}; err = zvec_doc_add_field_by_value(doc, "bin64_vec_field_ptr", ZVEC_DATA_TYPE_VECTOR_BINARY64, bin64_vec_data, sizeof(bin64_vec_data)); TEST_ASSERT(err == ZVEC_OK); // VECTOR_FP16 uint16_t fp16_vec_data[] = {0x3C00, 0x4000, 0x4200, 0x4400}; err = zvec_doc_add_field_by_value(doc, "fp16_vec_field_ptr", ZVEC_DATA_TYPE_VECTOR_FP16, fp16_vec_data, sizeof(fp16_vec_data)); TEST_ASSERT(err == ZVEC_OK); // VECTOR_FP64 double fp64_vec_data[] = {1.1, 2.2, 3.3, 4.4}; err = zvec_doc_add_field_by_value(doc, "fp64_vec_field_ptr", ZVEC_DATA_TYPE_VECTOR_FP64, fp64_vec_data, sizeof(fp64_vec_data)); TEST_ASSERT(err == ZVEC_OK); // VECTOR_INT8 int8_t int8_vec_data[] = {-128, -1, 0, 1, 127}; err = zvec_doc_add_field_by_value(doc, "int8_vec_field_ptr", ZVEC_DATA_TYPE_VECTOR_INT8, int8_vec_data, sizeof(int8_vec_data)); TEST_ASSERT(err == ZVEC_OK); // VECTOR_INT16 int16_t int16_vec_data[] = {-32768, -1, 0, 1, 32767}; err = zvec_doc_add_field_by_value(doc, "int16_vec_field_ptr", ZVEC_DATA_TYPE_VECTOR_INT16, int16_vec_data, sizeof(int16_vec_data)); TEST_ASSERT(err == ZVEC_OK); // ARRAY_INT32 int32_t array_int32_data[] = {100, 200, 300}; err = zvec_doc_add_field_by_value(doc, "array_int32_field_ptr", ZVEC_DATA_TYPE_ARRAY_INT32, array_int32_data, sizeof(array_int32_data)); TEST_ASSERT(err == ZVEC_OK); // ARRAY_INT64 int64_t array_int64_data[] = {-9223372036854775807LL, 0, 9223372036854775807LL}; err = zvec_doc_add_field_by_value(doc, "array_int64_field_ptr", ZVEC_DATA_TYPE_ARRAY_INT64, array_int64_data, sizeof(array_int64_data)); TEST_ASSERT(err == ZVEC_OK); // ARRAY_UINT32 uint32_t array_uint32_data[] = {0U, 1000000U, 4000000000U}; err = zvec_doc_add_field_by_value( doc, "array_uint32_field_ptr", ZVEC_DATA_TYPE_ARRAY_UINT32, array_uint32_data, sizeof(array_uint32_data)); TEST_ASSERT(err == ZVEC_OK); // ARRAY_UINT64 uint64_t array_uint64_data[] = {0ULL, 1000000000000ULL, 18000000000000000000ULL}; err = zvec_doc_add_field_by_value( doc, "array_uint64_field_ptr", ZVEC_DATA_TYPE_ARRAY_UINT64, array_uint64_data, sizeof(array_uint64_data)); TEST_ASSERT(err == ZVEC_OK); // ARRAY_FLOAT float array_float_data[] = {1.5f, 2.5f, 3.5f}; err = zvec_doc_add_field_by_value(doc, "array_float_field_ptr", ZVEC_DATA_TYPE_ARRAY_FLOAT, array_float_data, sizeof(array_float_data)); TEST_ASSERT(err == ZVEC_OK); // ARRAY_DOUBLE double array_double_data[] = {1.111111, 2.222222, 3.333333}; err = zvec_doc_add_field_by_value( doc, "array_double_field_ptr", ZVEC_DATA_TYPE_ARRAY_DOUBLE, array_double_data, sizeof(array_double_data)); TEST_ASSERT(err == ZVEC_OK); printf( "=== Testing zvec_doc_get_field_value_pointer with all supported types " "===\n"); // Test pointer access to BOOL const void *bool_ptr; size_t bool_ptr_size; err = zvec_doc_get_field_value_pointer(doc, "bool_field", ZVEC_DATA_TYPE_BOOL, &bool_ptr, &bool_ptr_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(bool_ptr != NULL); TEST_ASSERT(bool_ptr_size == sizeof(bool)); TEST_ASSERT(*(const bool *)bool_ptr == true); // Test pointer access to INT32 const void *int32_ptr; size_t int32_ptr_size; err = zvec_doc_get_field_value_pointer( doc, "int32_field", ZVEC_DATA_TYPE_INT32, &int32_ptr, &int32_ptr_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(int32_ptr != NULL); TEST_ASSERT(int32_ptr_size == sizeof(int32_t)); TEST_ASSERT(*(const int32_t *)int32_ptr == -2147483648); // Test pointer access to STRING const void *string_ptr; size_t string_ptr_size; err = zvec_doc_get_field_value_pointer(doc, "string_field", ZVEC_DATA_TYPE_STRING, &string_ptr, &string_ptr_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(string_ptr != NULL); TEST_ASSERT(string_ptr_size == strlen("Hello, 世界!")); TEST_ASSERT(memcmp(string_ptr, "Hello, 世界!", string_ptr_size) == 0); // Test pointer access to BINARY const void *binary_ptr; size_t binary_ptr_size; err = zvec_doc_get_field_value_pointer(doc, "binary_field", ZVEC_DATA_TYPE_BINARY, &binary_ptr, &binary_ptr_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(binary_ptr != NULL); TEST_ASSERT(binary_ptr_size == 6); TEST_ASSERT(memcmp(binary_ptr, "\x00\x01\x02\xFF\xFE\xFD", binary_ptr_size) == 0); // Test pointer access to VECTOR_FP32 const void *fp32_vec_ptr; size_t fp32_vec_ptr_size; err = zvec_doc_get_field_value_pointer(doc, "fp32_vec_field", ZVEC_DATA_TYPE_VECTOR_FP32, &fp32_vec_ptr, &fp32_vec_ptr_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(fp32_vec_ptr != NULL); TEST_ASSERT(fp32_vec_ptr_size == 5 * sizeof(float)); TEST_ASSERT(memcmp(fp32_vec_ptr, test_vector, fp32_vec_ptr_size) == 0); // Test pointer access to INT64 const void *int64_ptr; size_t int64_ptr_size; err = zvec_doc_get_field_value_pointer(doc, "int64_field_ptr", ZVEC_DATA_TYPE_INT64, &int64_ptr, &int64_ptr_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(int64_ptr != NULL); TEST_ASSERT(int64_ptr_size == sizeof(int64_t)); TEST_ASSERT(*(const int64_t *)int64_ptr == -9223372036854775807LL); // Test pointer access to UINT32 const void *uint32_ptr; size_t uint32_ptr_size; err = zvec_doc_get_field_value_pointer(doc, "uint32_field_ptr", ZVEC_DATA_TYPE_UINT32, &uint32_ptr, &uint32_ptr_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(uint32_ptr != NULL); TEST_ASSERT(uint32_ptr_size == sizeof(uint32_t)); TEST_ASSERT(*(const uint32_t *)uint32_ptr == 4000000000U); // Test pointer access to UINT64 const void *uint64_ptr; size_t uint64_ptr_size; err = zvec_doc_get_field_value_pointer(doc, "uint64_field_ptr", ZVEC_DATA_TYPE_UINT64, &uint64_ptr, &uint64_ptr_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(uint64_ptr != NULL); TEST_ASSERT(uint64_ptr_size == sizeof(uint64_t)); TEST_ASSERT(*(const uint64_t *)uint64_ptr == 18000000000000000000ULL); // Test pointer access to FLOAT const void *float_ptr; size_t float_ptr_size; err = zvec_doc_get_field_value_pointer(doc, "float_field_ptr", ZVEC_DATA_TYPE_FLOAT, &float_ptr, &float_ptr_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(float_ptr != NULL); TEST_ASSERT(float_ptr_size == sizeof(float)); TEST_ASSERT(fabs(*(const float *)float_ptr - 3.14159265f) < 1e-6f); // Test pointer access to DOUBLE const void *double_ptr; size_t double_ptr_size; err = zvec_doc_get_field_value_pointer(doc, "double_field_ptr", ZVEC_DATA_TYPE_DOUBLE, &double_ptr, &double_ptr_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(double_ptr != NULL); TEST_ASSERT(double_ptr_size == sizeof(double)); TEST_ASSERT(fabs(*(const double *)double_ptr - 2.718281828459045) < 1e-15); // Test pointer access to VECTOR_BINARY64 const void *bin64_vec_ptr; size_t bin64_vec_ptr_size; err = zvec_doc_get_field_value_pointer(doc, "bin64_vec_field_ptr", ZVEC_DATA_TYPE_VECTOR_BINARY64, &bin64_vec_ptr, &bin64_vec_ptr_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(bin64_vec_ptr != NULL); TEST_ASSERT(bin64_vec_ptr_size == sizeof(bin64_vec_data)); TEST_ASSERT(memcmp(bin64_vec_ptr, bin64_vec_data, bin64_vec_ptr_size) == 0); // Test pointer access to VECTOR_FP16 const void *fp16_vec_ptr; size_t fp16_vec_ptr_size; err = zvec_doc_get_field_value_pointer(doc, "fp16_vec_field_ptr", ZVEC_DATA_TYPE_VECTOR_FP16, &fp16_vec_ptr, &fp16_vec_ptr_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(fp16_vec_ptr != NULL); TEST_ASSERT(fp16_vec_ptr_size == sizeof(fp16_vec_data)); TEST_ASSERT(memcmp(fp16_vec_ptr, fp16_vec_data, fp16_vec_ptr_size) == 0); // Test pointer access to VECTOR_FP64 const void *fp64_vec_ptr; size_t fp64_vec_ptr_size; err = zvec_doc_get_field_value_pointer(doc, "fp64_vec_field_ptr", ZVEC_DATA_TYPE_VECTOR_FP64, &fp64_vec_ptr, &fp64_vec_ptr_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(fp64_vec_ptr != NULL); TEST_ASSERT(fp64_vec_ptr_size == sizeof(fp64_vec_data)); TEST_ASSERT(memcmp(fp64_vec_ptr, fp64_vec_data, fp64_vec_ptr_size) == 0); // Test pointer access to VECTOR_INT8 const void *int8_vec_ptr; size_t int8_vec_ptr_size; err = zvec_doc_get_field_value_pointer(doc, "int8_vec_field_ptr", ZVEC_DATA_TYPE_VECTOR_INT8, &int8_vec_ptr, &int8_vec_ptr_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(int8_vec_ptr != NULL); TEST_ASSERT(int8_vec_ptr_size == sizeof(int8_vec_data)); TEST_ASSERT(memcmp(int8_vec_ptr, int8_vec_data, int8_vec_ptr_size) == 0); // Test pointer access to VECTOR_INT16 const void *int16_vec_ptr; size_t int16_vec_ptr_size; err = zvec_doc_get_field_value_pointer(doc, "int16_vec_field_ptr", ZVEC_DATA_TYPE_VECTOR_INT16, &int16_vec_ptr, &int16_vec_ptr_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(int16_vec_ptr != NULL); TEST_ASSERT(int16_vec_ptr_size == sizeof(int16_vec_data)); TEST_ASSERT(memcmp(int16_vec_ptr, int16_vec_data, int16_vec_ptr_size) == 0); // Test pointer access to ARRAY_INT32 const void *array_int32_ptr; size_t array_int32_ptr_size; err = zvec_doc_get_field_value_pointer( doc, "array_int32_field_ptr", ZVEC_DATA_TYPE_ARRAY_INT32, &array_int32_ptr, &array_int32_ptr_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(array_int32_ptr != NULL); TEST_ASSERT(array_int32_ptr_size == sizeof(array_int32_data)); TEST_ASSERT(((const int32_t *)array_int32_ptr)[0] == 100); TEST_ASSERT(((const int32_t *)array_int32_ptr)[1] == 200); TEST_ASSERT(((const int32_t *)array_int32_ptr)[2] == 300); // Test pointer access to ARRAY_INT64 const void *array_int64_ptr; size_t array_int64_ptr_size; err = zvec_doc_get_field_value_pointer( doc, "array_int64_field_ptr", ZVEC_DATA_TYPE_ARRAY_INT64, &array_int64_ptr, &array_int64_ptr_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(array_int64_ptr != NULL); TEST_ASSERT(array_int64_ptr_size == sizeof(array_int64_data)); TEST_ASSERT(((const int64_t *)array_int64_ptr)[0] == -9223372036854775807LL); TEST_ASSERT(((const int64_t *)array_int64_ptr)[1] == 0); TEST_ASSERT(((const int64_t *)array_int64_ptr)[2] == 9223372036854775807LL); // Test pointer access to ARRAY_UINT32 const void *array_uint32_ptr; size_t array_uint32_ptr_size; err = zvec_doc_get_field_value_pointer( doc, "array_uint32_field_ptr", ZVEC_DATA_TYPE_ARRAY_UINT32, &array_uint32_ptr, &array_uint32_ptr_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(array_uint32_ptr != NULL); TEST_ASSERT(array_uint32_ptr_size == sizeof(array_uint32_data)); TEST_ASSERT(((const uint32_t *)array_uint32_ptr)[0] == 0U); TEST_ASSERT(((const uint32_t *)array_uint32_ptr)[1] == 1000000U); TEST_ASSERT(((const uint32_t *)array_uint32_ptr)[2] == 4000000000U); // Test pointer access to ARRAY_UINT64 const void *array_uint64_ptr; size_t array_uint64_ptr_size; err = zvec_doc_get_field_value_pointer( doc, "array_uint64_field_ptr", ZVEC_DATA_TYPE_ARRAY_UINT64, &array_uint64_ptr, &array_uint64_ptr_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(array_uint64_ptr != NULL); TEST_ASSERT(array_uint64_ptr_size == sizeof(array_uint64_data)); TEST_ASSERT(((const uint64_t *)array_uint64_ptr)[0] == 0ULL); TEST_ASSERT(((const uint64_t *)array_uint64_ptr)[1] == 1000000000000ULL); TEST_ASSERT(((const uint64_t *)array_uint64_ptr)[2] == 18000000000000000000ULL); // Test pointer access to ARRAY_FLOAT const void *array_float_ptr; size_t array_float_ptr_size; err = zvec_doc_get_field_value_pointer( doc, "array_float_field_ptr", ZVEC_DATA_TYPE_ARRAY_FLOAT, &array_float_ptr, &array_float_ptr_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(array_float_ptr != NULL); TEST_ASSERT(array_float_ptr_size == sizeof(array_float_data)); TEST_ASSERT(((const float *)array_float_ptr)[0] == 1.5f); TEST_ASSERT(((const float *)array_float_ptr)[1] == 2.5f); TEST_ASSERT(((const float *)array_float_ptr)[2] == 3.5f); // Test pointer access to ARRAY_DOUBLE const void *array_double_ptr; size_t array_double_ptr_size; err = zvec_doc_get_field_value_pointer( doc, "array_double_field_ptr", ZVEC_DATA_TYPE_ARRAY_DOUBLE, &array_double_ptr, &array_double_ptr_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(array_double_ptr != NULL); TEST_ASSERT(array_double_ptr_size == sizeof(array_double_data)); TEST_ASSERT(fabs(((const double *)array_double_ptr)[0] - 1.111111) < 1e-10); TEST_ASSERT(fabs(((const double *)array_double_ptr)[1] - 2.222222) < 1e-10); TEST_ASSERT(fabs(((const double *)array_double_ptr)[2] - 3.333333) < 1e-10); zvec_free(string_field.value.string_value.data); zvec_free(binary_field.value.string_value.data); zvec_doc_destroy(doc); TEST_END(); } void test_doc_field_operations(void) { TEST_START(); zvec_doc_t *doc = zvec_doc_create(); TEST_ASSERT(doc != NULL); zvec_error_code_t err; // Add some fields zvec_doc_field_t bool_field; bool_field.name.data = "bool_field"; bool_field.name.length = strlen("bool_field"); bool_field.data_type = ZVEC_DATA_TYPE_BOOL; bool_field.value.bool_value = true; err = zvec_doc_add_field_by_struct(doc, &bool_field); TEST_ASSERT(err == ZVEC_OK); zvec_doc_field_t int32_field; int32_field.name.data = "int32_field"; int32_field.name.length = strlen("int32_field"); int32_field.data_type = ZVEC_DATA_TYPE_INT32; int32_field.value.int32_value = -2147483648; err = zvec_doc_add_field_by_struct(doc, &int32_field); TEST_ASSERT(err == ZVEC_OK); zvec_doc_field_t string_field; string_field.name.data = "string_field"; string_field.name.length = strlen("string_field"); string_field.data_type = ZVEC_DATA_TYPE_STRING; string_field.value.string_value = *zvec_string_create("Hello"); err = zvec_doc_add_field_by_struct(doc, &string_field); TEST_ASSERT(err == ZVEC_OK); // Test field count size_t field_count = zvec_doc_get_field_count(doc); TEST_ASSERT(field_count >= 3); // Test field existence checks TEST_ASSERT(zvec_doc_has_field(doc, "bool_field") == true); TEST_ASSERT(zvec_doc_has_field(doc, "int32_field") == true); TEST_ASSERT(zvec_doc_has_field(doc, "string_field") == true); TEST_ASSERT(zvec_doc_has_field(doc, "nonexistent") == false); TEST_ASSERT(zvec_doc_has_field_value(doc, "bool_field") == true); TEST_ASSERT(zvec_doc_is_field_null(doc, "bool_field") == false); TEST_ASSERT(zvec_doc_is_field_null(doc, "nonexistent") == false); // Test field names retrieval char **field_names; size_t name_count; err = zvec_doc_get_field_names(doc, &field_names, &name_count); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(name_count >= 3); TEST_ASSERT(field_names != NULL); // Verify some expected fields are present bool found_key_fields = false; for (size_t i = 0; i < name_count; i++) { if (strcmp(field_names[i], "bool_field") == 0 || strcmp(field_names[i], "int32_field") == 0 || strcmp(field_names[i], "string_field") == 0) { found_key_fields = true; break; } } TEST_ASSERT(found_key_fields == true); zvec_free_str_array(field_names, name_count); zvec_free(string_field.value.string_value.data); zvec_doc_destroy(doc); TEST_END(); } void test_doc_error_conditions(void) { TEST_START(); zvec_doc_t *doc = zvec_doc_create(); TEST_ASSERT(doc != NULL); // Add a field for error testing zvec_doc_field_t bool_field; bool_field.name.data = "bool_field"; bool_field.name.length = strlen("bool_field"); bool_field.data_type = ZVEC_DATA_TYPE_BOOL; bool_field.value.bool_value = true; zvec_doc_add_field_by_struct(doc, &bool_field); zvec_error_code_t err; const void *dummy_ptr; size_t dummy_ptr_size; int32_t int32_result; void *string_result; size_t string_size; printf("=== Testing error conditions ===\n"); // Test non-existent field err = zvec_doc_get_field_value_basic(doc, "missing_field", ZVEC_DATA_TYPE_INT32, &int32_result, sizeof(int32_result)); TEST_ASSERT(err != ZVEC_OK); err = zvec_doc_get_field_value_copy(doc, "missing_field", ZVEC_DATA_TYPE_STRING, &string_result, &string_size); TEST_ASSERT(err != ZVEC_OK); err = zvec_doc_get_field_value_pointer( doc, "missing_field", ZVEC_DATA_TYPE_FLOAT, &dummy_ptr, &dummy_ptr_size); TEST_ASSERT(err != ZVEC_OK); // Test wrong data type access err = zvec_doc_get_field_value_basic(doc, "bool_field", ZVEC_DATA_TYPE_INT32, &int32_result, sizeof(int32_result)); TEST_ASSERT(err != ZVEC_OK); err = zvec_doc_get_field_value_copy(doc, "bool_field", ZVEC_DATA_TYPE_STRING, &string_result, &string_size); TEST_ASSERT(err != ZVEC_OK); err = zvec_doc_get_field_value_pointer( doc, "bool_field", ZVEC_DATA_TYPE_FLOAT, &dummy_ptr, &dummy_ptr_size); TEST_ASSERT(err != ZVEC_OK); zvec_doc_destroy(doc); TEST_END(); } void test_doc_serialization(void) { TEST_START(); zvec_doc_t *doc = zvec_doc_create(); TEST_ASSERT(doc != NULL); zvec_error_code_t err; // Add fields for serialization testing zvec_doc_field_t int32_field; int32_field.name.data = "int32_field"; int32_field.name.length = strlen("int32_field"); int32_field.data_type = ZVEC_DATA_TYPE_INT32; int32_field.value.int32_value = -2147483648; err = zvec_doc_add_field_by_struct(doc, &int32_field); TEST_ASSERT(err == ZVEC_OK); zvec_doc_field_t string_field; string_field.name.data = "string_field"; string_field.name.length = strlen("string_field"); string_field.data_type = ZVEC_DATA_TYPE_STRING; string_field.value.string_value = *zvec_string_create("Serialization Test"); err = zvec_doc_add_field_by_struct(doc, &string_field); TEST_ASSERT(err == ZVEC_OK); printf("=== Testing document serialization ===\n"); uint8_t *serialized_data; size_t data_size; err = zvec_doc_serialize(doc, &serialized_data, &data_size); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(serialized_data != NULL); TEST_ASSERT(data_size > 0); zvec_doc_t *deserialized_doc; err = zvec_doc_deserialize(serialized_data, data_size, &deserialized_doc); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(deserialized_doc != NULL); // Verify deserialized document has same field count size_t field_count = zvec_doc_get_field_count(doc); size_t deserialized_field_count = zvec_doc_get_field_count(deserialized_doc); TEST_ASSERT(deserialized_field_count == field_count); // Test a field from deserialized document int32_t deserialized_int32; err = zvec_doc_get_field_value_basic( deserialized_doc, "int32_field", ZVEC_DATA_TYPE_INT32, &deserialized_int32, sizeof(deserialized_int32)); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(deserialized_int32 == -2147483648); zvec_free_uint8_array(serialized_data); free(string_field.value.string_value.data); zvec_doc_destroy(deserialized_doc); zvec_doc_destroy(doc); TEST_END(); } // ============================================================================= // Index parameter tests // ============================================================================= void test_index_params(void) { TEST_START(); // Test HNSW parameter creation zvec_index_params_t *hnsw_params = zvec_test_create_default_hnsw_params(); TEST_ASSERT(hnsw_params != NULL); if (hnsw_params) { zvec_free(hnsw_params); } // Test Flat parameter creation zvec_index_params_t *flat_params = zvec_test_create_default_flat_params(); TEST_ASSERT(flat_params != NULL); if (flat_params) { zvec_free(flat_params); } // Test scalar index parameter creation zvec_index_params_t *invert_params = zvec_test_create_default_invert_params(true); TEST_ASSERT(invert_params != NULL); if (invert_params) { zvec_free(invert_params); } TEST_END(); } // ============================================================================= // Memory management tests // ============================================================================= void test_zvec_string_functions(void) { TEST_START(); // Test string creation and basic operations zvec_string_t *str1 = zvec_string_create("Hello World"); TEST_ASSERT(str1 != NULL); TEST_ASSERT(zvec_string_length(str1) == 11); TEST_ASSERT(strcmp(zvec_string_c_str(str1), "Hello World") == 0); // Test string copy zvec_string_t *str2 = zvec_string_copy(str1); TEST_ASSERT(str2 != NULL); TEST_ASSERT(zvec_string_length(str2) == 11); TEST_ASSERT(strcmp(zvec_string_c_str(str2), "Hello World") == 0); // Test string comparison int cmp_result = zvec_string_compare(str1, str2); TEST_ASSERT(cmp_result == 0); zvec_string_t *str3 = zvec_string_create("Hello"); TEST_ASSERT(zvec_string_compare(str1, str3) > 0); // Test string creation from view zvec_string_view_t view = {"Hello View", 10}; zvec_string_t *str4 = zvec_string_create_from_view(&view); TEST_ASSERT(str4 != NULL); TEST_ASSERT(zvec_string_length(str4) == 10); TEST_ASSERT(strcmp(zvec_string_c_str(str4), "Hello View") == 0); // Test string view with embedded null bytes char binary_data[] = {'H', 'e', 'l', 'l', 'o', '\0', 'W', 'o', 'r', 'l', 'd'}; zvec_string_view_t binary_view = {binary_data, 11}; zvec_string_t *str5 = zvec_string_create_from_view(&binary_view); TEST_ASSERT(str5 != NULL); TEST_ASSERT(zvec_string_length(str5) == 11); // Note: strcmp will stop at first null byte, so we need to compare manually TEST_ASSERT(memcmp(zvec_string_c_str(str5), binary_data, 11) == 0); // Cleanup zvec_free_string(str1); zvec_free_string(str2); zvec_free_string(str3); zvec_free_string(str4); zvec_free_string(str5); TEST_END(); } void test_index_params_functions(void) { TEST_START(); // Test index params with new opaque pointer API // Test HNSW params zvec_index_params_t *hnsw_params = zvec_index_params_create(ZVEC_INDEX_TYPE_HNSW); TEST_ASSERT(hnsw_params != NULL); TEST_ASSERT(zvec_index_params_get_type(hnsw_params) == ZVEC_INDEX_TYPE_HNSW); // Default metric type is L2, need to set it explicitly zvec_index_params_set_metric_type(hnsw_params, ZVEC_METRIC_TYPE_COSINE); TEST_ASSERT(zvec_index_params_get_metric_type(hnsw_params) == ZVEC_METRIC_TYPE_COSINE); int m, ef_construction; m = zvec_index_params_get_hnsw_m(hnsw_params); ef_construction = zvec_index_params_get_hnsw_ef_construction(hnsw_params); TEST_ASSERT(m == 50); TEST_ASSERT(ef_construction == 500); // Test invert index params zvec_index_params_t *invert_params = zvec_index_params_create(ZVEC_INDEX_TYPE_INVERT); TEST_ASSERT(invert_params != NULL); TEST_ASSERT(zvec_index_params_get_type(invert_params) == ZVEC_INDEX_TYPE_INVERT); bool enable_range_opt, enable_wildcard; zvec_index_params_get_invert_params(invert_params, &enable_range_opt, &enable_wildcard); TEST_ASSERT(enable_range_opt == true); // Default is true TEST_ASSERT(enable_wildcard == false); // Default is false // Test flat index params zvec_index_params_t *flat_params = zvec_index_params_create(ZVEC_INDEX_TYPE_FLAT); TEST_ASSERT(flat_params != NULL); TEST_ASSERT(zvec_index_params_get_type(flat_params) == ZVEC_INDEX_TYPE_FLAT); // Default metric type is L2, need to set it explicitly zvec_index_params_set_metric_type(flat_params, ZVEC_METRIC_TYPE_IP); TEST_ASSERT(zvec_index_params_get_metric_type(flat_params) == ZVEC_METRIC_TYPE_IP); // Test IVF index params zvec_index_params_t *ivf_params = zvec_index_params_create(ZVEC_INDEX_TYPE_IVF); TEST_ASSERT(ivf_params != NULL); TEST_ASSERT(zvec_index_params_get_type(ivf_params) == ZVEC_INDEX_TYPE_IVF); // Default metric type is L2 TEST_ASSERT(zvec_index_params_get_metric_type(ivf_params) == ZVEC_METRIC_TYPE_L2); int n_list, n_iters; bool use_soar; zvec_index_params_get_ivf_params(ivf_params, &n_list, &n_iters, &use_soar); TEST_ASSERT(n_list == 1024); TEST_ASSERT(n_iters == 10); TEST_ASSERT(use_soar == false); // Default is false // Test DiskANN index params zvec_index_params_t *diskann_params = zvec_index_params_create(ZVEC_INDEX_TYPE_DISKANN); TEST_ASSERT(diskann_params != NULL); TEST_ASSERT(zvec_index_params_get_type(diskann_params) == ZVEC_INDEX_TYPE_DISKANN); TEST_ASSERT(zvec_index_params_get_diskann_max_degree(diskann_params) == 100); TEST_ASSERT(zvec_index_params_get_diskann_list_size(diskann_params) == 50); TEST_ASSERT(zvec_index_params_get_diskann_pq_chunk_num(diskann_params) == 0); // Cleanup zvec_index_params_destroy(hnsw_params); zvec_index_params_destroy(invert_params); zvec_index_params_destroy(flat_params); zvec_index_params_destroy(ivf_params); zvec_index_params_destroy(diskann_params); TEST_END(); } void test_quantizer_enable_rotate(void) { TEST_START(); // Test 1: set enable_rotate=true on HNSW params and verify zvec_index_params_t *hnsw_params = zvec_index_params_create(ZVEC_INDEX_TYPE_HNSW); TEST_ASSERT(hnsw_params != NULL); // Default should be false TEST_ASSERT(zvec_index_params_get_quantizer_enable_rotate(hnsw_params) == false); // Set to true and verify zvec_error_code_t err = zvec_index_params_set_quantizer_enable_rotate(hnsw_params, true); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(zvec_index_params_get_quantizer_enable_rotate(hnsw_params) == true); // Set back to false and verify err = zvec_index_params_set_quantizer_enable_rotate(hnsw_params, false); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(zvec_index_params_get_quantizer_enable_rotate(hnsw_params) == false); zvec_index_params_destroy(hnsw_params); // Test 2: set enable_rotate on FLAT index params (also a vector index) zvec_index_params_t *flat_params = zvec_index_params_create(ZVEC_INDEX_TYPE_FLAT); TEST_ASSERT(flat_params != NULL); err = zvec_index_params_set_quantizer_enable_rotate(flat_params, true); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(zvec_index_params_get_quantizer_enable_rotate(flat_params) == true); zvec_index_params_destroy(flat_params); // Test 3: set enable_rotate on non-vector index (INVERT) should fail zvec_index_params_t *invert_params = zvec_index_params_create(ZVEC_INDEX_TYPE_INVERT); TEST_ASSERT(invert_params != NULL); err = zvec_index_params_set_quantizer_enable_rotate(invert_params, true); TEST_ASSERT(err != ZVEC_OK); zvec_index_params_destroy(invert_params); // Test 4: NULL params should return false for getter TEST_ASSERT(zvec_index_params_get_quantizer_enable_rotate(NULL) == false); // Test 5: NULL params should return error for setter err = zvec_index_params_set_quantizer_enable_rotate(NULL, true); TEST_ASSERT(err != ZVEC_OK); TEST_END(); } void test_int8_rotate_e2e(void) { TEST_START(); char temp_dir[] = "./zvec_test_int8_rotate_e2e"; const size_t dim = 128; const size_t cnt = 2000; const size_t topk = 10; // Create schema with HNSW + INT8 + enable_rotate zvec_collection_schema_t *schema = zvec_collection_schema_create("int8_rotate_test"); TEST_ASSERT(schema != NULL); // Add ID field zvec_field_schema_t *id_field = zvec_field_schema_create("id", ZVEC_DATA_TYPE_INT64, false, 0); zvec_collection_schema_add_field(schema, id_field); // Add vector field with HNSW + INT8 + rotate zvec_index_params_t *hnsw_params = zvec_index_params_create(ZVEC_INDEX_TYPE_HNSW); TEST_ASSERT(hnsw_params != NULL); zvec_index_params_set_metric_type(hnsw_params, ZVEC_METRIC_TYPE_L2); zvec_index_params_set_hnsw_params(hnsw_params, 16, 100); zvec_index_params_set_quantize_type(hnsw_params, ZVEC_QUANTIZE_TYPE_INT8); zvec_index_params_set_quantizer_enable_rotate(hnsw_params, true); zvec_field_schema_t *vec_field = zvec_field_schema_create( "embedding", ZVEC_DATA_TYPE_VECTOR_FP32, false, dim); zvec_field_schema_set_index_params(vec_field, hnsw_params); zvec_collection_schema_add_field(schema, vec_field); zvec_index_params_destroy(hnsw_params); // Create and open collection zvec_collection_t *collection = NULL; zvec_error_code_t err = zvec_collection_create_and_open(temp_dir, schema, NULL, &collection); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(collection != NULL); // Insert 2000 random vectors srand(42); for (size_t i = 0; i < cnt; i++) { float *vec = (float *)malloc(dim * sizeof(float)); TEST_ASSERT(vec != NULL); for (size_t j = 0; j < dim; j++) { vec[j] = (float)rand() / (float)RAND_MAX * 2.0f - 1.0f; } zvec_doc_t *doc = zvec_doc_create(); zvec_doc_set_pk(doc, zvec_test_make_pk(i + 1)); zvec_doc_add_field_by_value(doc, "id", ZVEC_DATA_TYPE_INT64, &(int64_t){(int64_t)(i + 1)}, sizeof(int64_t)); zvec_doc_add_field_by_value(doc, "embedding", ZVEC_DATA_TYPE_VECTOR_FP32, vec, dim * sizeof(float)); size_t success_count, error_count; const zvec_doc_t *docs[] = {doc}; err = zvec_collection_insert(collection, docs, 1, &success_count, &error_count); TEST_ASSERT(err == ZVEC_OK); zvec_doc_destroy(doc); free(vec); } // Flush to build index zvec_collection_flush(collection); // Search float *query = (float *)malloc(dim * sizeof(float)); TEST_ASSERT(query != NULL); for (size_t j = 0; j < dim; j++) { query[j] = (float)rand() / (float)RAND_MAX * 2.0f - 1.0f; } zvec_vector_query_t *vq = zvec_vector_query_create(); TEST_ASSERT(vq != NULL); zvec_vector_query_set_field_name(vq, "embedding"); zvec_vector_query_set_query_vector(vq, query, dim * sizeof(float)); zvec_vector_query_set_topk(vq, topk); zvec_doc_t **results = NULL; size_t result_count = 0; err = zvec_collection_query(collection, vq, &results, &result_count); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(result_count > 0); printf(" [int8_rotate_e2e] first search returned %zu results\n", result_count); zvec_docs_free(results, result_count); // Close and reopen zvec_collection_close(collection); collection = NULL; err = zvec_collection_open(temp_dir, NULL, &collection); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(collection != NULL); // Search again after reopen (rotator should auto-load from storage) results = NULL; result_count = 0; err = zvec_collection_query(collection, vq, &results, &result_count); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(result_count > 0); printf(" [int8_rotate_e2e] reopen search returned %zu results\n", result_count); zvec_docs_free(results, result_count); // Cleanup zvec_vector_query_destroy(vq); zvec_collection_destroy(collection); zvec_collection_schema_destroy(schema); free(query); cleanup_temp_directory(temp_dir); TEST_END(); } void test_index_params_api_functions(void) { TEST_START(); // Test zvec_index_params_create for HNSW zvec_index_params_t *hnsw_params = zvec_index_params_create(ZVEC_INDEX_TYPE_HNSW); TEST_ASSERT(hnsw_params != NULL); TEST_ASSERT(zvec_index_params_get_type(hnsw_params) == ZVEC_INDEX_TYPE_HNSW); TEST_ASSERT(zvec_index_params_get_metric_type(hnsw_params) == ZVEC_METRIC_TYPE_L2); // Test zvec_index_params_set_metric_type zvec_index_params_set_metric_type(hnsw_params, ZVEC_METRIC_TYPE_COSINE); TEST_ASSERT(zvec_index_params_get_metric_type(hnsw_params) == ZVEC_METRIC_TYPE_COSINE); // Test zvec_index_params_set_hnsw_params zvec_index_params_set_hnsw_params(hnsw_params, 32, 300); int m, ef_construction; m = zvec_index_params_get_hnsw_m(hnsw_params); ef_construction = zvec_index_params_get_hnsw_ef_construction(hnsw_params); TEST_ASSERT(m == 32); TEST_ASSERT(ef_construction == 300); // Test zvec_index_params_create for IVF zvec_index_params_t *ivf_params = zvec_index_params_create(ZVEC_INDEX_TYPE_IVF); TEST_ASSERT(ivf_params != NULL); TEST_ASSERT(zvec_index_params_get_type(ivf_params) == ZVEC_INDEX_TYPE_IVF); TEST_ASSERT(zvec_index_params_get_metric_type(ivf_params) == ZVEC_METRIC_TYPE_L2); // Test zvec_index_params_set_ivf_params zvec_index_params_set_ivf_params(ivf_params, 200, 20, true); int n_list, n_iters; bool use_soar; zvec_index_params_get_ivf_params(ivf_params, &n_list, &n_iters, &use_soar); TEST_ASSERT(n_list == 200); TEST_ASSERT(n_iters == 20); TEST_ASSERT(use_soar == true); // Test zvec_index_params_create for INVERT zvec_index_params_t *invert_params = zvec_index_params_create(ZVEC_INDEX_TYPE_INVERT); TEST_ASSERT(invert_params != NULL); TEST_ASSERT(zvec_index_params_get_type(invert_params) == ZVEC_INDEX_TYPE_INVERT); // Test zvec_index_params_set_invert_params zvec_index_params_set_invert_params(invert_params, true, true); bool enable_range_opt, enable_wildcard; zvec_index_params_get_invert_params(invert_params, &enable_range_opt, &enable_wildcard); TEST_ASSERT(enable_range_opt == true); TEST_ASSERT(enable_wildcard == true); // Test zvec_index_params_create for FLAT zvec_index_params_t *flat_params = zvec_index_params_create(ZVEC_INDEX_TYPE_FLAT); TEST_ASSERT(flat_params != NULL); TEST_ASSERT(zvec_index_params_get_type(flat_params) == ZVEC_INDEX_TYPE_FLAT); zvec_index_params_set_metric_type(flat_params, ZVEC_METRIC_TYPE_IP); TEST_ASSERT(zvec_index_params_get_metric_type(flat_params) == ZVEC_METRIC_TYPE_IP); // Test zvec_index_params_create for DiskANN zvec_index_params_t *diskann_params = zvec_index_params_create(ZVEC_INDEX_TYPE_DISKANN); TEST_ASSERT(diskann_params != NULL); TEST_ASSERT(zvec_index_params_get_type(diskann_params) == ZVEC_INDEX_TYPE_DISKANN); TEST_ASSERT(zvec_index_params_get_metric_type(diskann_params) == ZVEC_METRIC_TYPE_L2); // Test zvec_index_params_set_diskann_params zvec_index_params_set_diskann_params(diskann_params, 200, 100, 8); TEST_ASSERT(zvec_index_params_get_diskann_max_degree(diskann_params) == 200); TEST_ASSERT(zvec_index_params_get_diskann_list_size(diskann_params) == 100); TEST_ASSERT(zvec_index_params_get_diskann_pq_chunk_num(diskann_params) == 8); // Cleanup zvec_index_params_destroy(hnsw_params); zvec_index_params_destroy(ivf_params); zvec_index_params_destroy(invert_params); zvec_index_params_destroy(flat_params); zvec_index_params_destroy(diskann_params); TEST_END(); } void test_utility_functions(void) { TEST_START(); // Test error code to string conversion const char *error_str = zvec_error_code_to_string(ZVEC_OK); TEST_ASSERT(error_str != NULL); TEST_ASSERT(strlen(error_str) > 0); error_str = zvec_error_code_to_string(ZVEC_ERROR_INVALID_ARGUMENT); TEST_ASSERT(error_str != NULL); // Test data type to string conversion const char *data_type_str = zvec_data_type_to_string(ZVEC_DATA_TYPE_INT32); TEST_ASSERT(data_type_str != NULL); TEST_ASSERT(strlen(data_type_str) > 0); data_type_str = zvec_data_type_to_string(ZVEC_DATA_TYPE_STRING); TEST_ASSERT(data_type_str != NULL); // Test index type to string conversion const char *index_type_str = zvec_index_type_to_string(ZVEC_INDEX_TYPE_HNSW); TEST_ASSERT(index_type_str != NULL); TEST_ASSERT(strlen(index_type_str) > 0); index_type_str = zvec_index_type_to_string(ZVEC_INDEX_TYPE_INVERT); TEST_ASSERT(index_type_str != NULL); TEST_END(); } void test_memory_management_functions(void) { TEST_START(); // Test string allocation and deallocation zvec_string_t *str = zvec_string_create("Test String"); TEST_ASSERT(str != NULL); zvec_free_string(str); void *buffer = malloc(64); TEST_ASSERT(buffer != NULL); zvec_free(buffer); TEST_END(); } void test_query_params_functions(void) { TEST_START(); // Test HNSW query parameters zvec_hnsw_query_params_t *hnsw_params = zvec_query_params_hnsw_create(50, 0.5f, false, true); TEST_ASSERT(hnsw_params != NULL); // Test IVF query parameters zvec_ivf_query_params_t *ivf_params = zvec_query_params_ivf_create(10, true, 1.5f); TEST_ASSERT(ivf_params != NULL); // Test Flat query parameters zvec_flat_query_params_t *flat_params = zvec_query_params_flat_create(false, 2.0f); TEST_ASSERT(flat_params != NULL); // Test DiskANN query parameters zvec_diskann_query_params_t *diskann_params = zvec_query_params_diskann_create(500); TEST_ASSERT(diskann_params != NULL); zvec_error_code_t err; // Test HNSW-specific parameters err = zvec_query_params_hnsw_set_ef(hnsw_params, 75); TEST_ASSERT(err == ZVEC_OK); // Test HNSW common parameters (radius, is_linear, is_using_refiner) err = zvec_query_params_hnsw_set_radius(hnsw_params, 0.8f); TEST_ASSERT(err == ZVEC_OK); float radius = zvec_query_params_hnsw_get_radius(hnsw_params); TEST_ASSERT(radius == 0.8f); err = zvec_query_params_hnsw_set_is_linear(hnsw_params, false); TEST_ASSERT(err == ZVEC_OK); bool is_linear = zvec_query_params_hnsw_get_is_linear(hnsw_params); TEST_ASSERT(is_linear == false); err = zvec_query_params_hnsw_set_is_using_refiner(hnsw_params, true); TEST_ASSERT(err == ZVEC_OK); bool is_using_refiner = zvec_query_params_hnsw_get_is_using_refiner(hnsw_params); TEST_ASSERT(is_using_refiner == true); // Test IVF-specific parameters err = zvec_query_params_ivf_set_nprobe(ivf_params, 15); TEST_ASSERT(err == ZVEC_OK); // Test IVF scale factor setting err = zvec_query_params_ivf_set_scale_factor(ivf_params, 2.5f); TEST_ASSERT(err == ZVEC_OK); // Test IVF common parameters (radius, is_linear, is_using_refiner) err = zvec_query_params_ivf_set_radius(ivf_params, 0.9f); TEST_ASSERT(err == ZVEC_OK); radius = zvec_query_params_ivf_get_radius(ivf_params); TEST_ASSERT(radius == 0.9f); err = zvec_query_params_ivf_set_is_linear(ivf_params, true); TEST_ASSERT(err == ZVEC_OK); is_linear = zvec_query_params_ivf_get_is_linear(ivf_params); TEST_ASSERT(is_linear == true); err = zvec_query_params_ivf_set_is_using_refiner(ivf_params, false); TEST_ASSERT(err == ZVEC_OK); is_using_refiner = zvec_query_params_ivf_get_is_using_refiner(ivf_params); TEST_ASSERT(is_using_refiner == false); // Test Flat scale factor setting err = zvec_query_params_flat_set_scale_factor(flat_params, 3.0f); TEST_ASSERT(err == ZVEC_OK); // Test Flat common parameters (radius, is_linear, is_using_refiner) err = zvec_query_params_flat_set_radius(flat_params, 0.7f); TEST_ASSERT(err == ZVEC_OK); radius = zvec_query_params_flat_get_radius(flat_params); TEST_ASSERT(radius == 0.7f); err = zvec_query_params_flat_set_is_linear(flat_params, true); TEST_ASSERT(err == ZVEC_OK); is_linear = zvec_query_params_flat_get_is_linear(flat_params); TEST_ASSERT(is_linear == true); err = zvec_query_params_flat_set_is_using_refiner(flat_params, true); TEST_ASSERT(err == ZVEC_OK); is_using_refiner = zvec_query_params_flat_get_is_using_refiner(flat_params); TEST_ASSERT(is_using_refiner == true); // Test Vamana query parameters zvec_vamana_query_params_t *vamana_params = zvec_query_params_vamana_create(256, 0.3f, false, true); TEST_ASSERT(vamana_params != NULL); TEST_ASSERT(zvec_query_params_vamana_get_ef_search(vamana_params) == 256); TEST_ASSERT(zvec_query_params_vamana_get_radius(vamana_params) == 0.3f); TEST_ASSERT(zvec_query_params_vamana_get_is_linear(vamana_params) == false); TEST_ASSERT(zvec_query_params_vamana_get_is_using_refiner(vamana_params) == true); // Vamana set/get all parameters err = zvec_query_params_vamana_set_ef_search(vamana_params, 512); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(zvec_query_params_vamana_get_ef_search(vamana_params) == 512); err = zvec_query_params_vamana_set_radius(vamana_params, 0.5f); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(zvec_query_params_vamana_get_radius(vamana_params) == 0.5f); err = zvec_query_params_vamana_set_is_linear(vamana_params, true); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(zvec_query_params_vamana_get_is_linear(vamana_params) == true); err = zvec_query_params_vamana_set_is_using_refiner(vamana_params, false); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(zvec_query_params_vamana_get_is_using_refiner(vamana_params) == false); // Test DiskANN-specific parameters TEST_ASSERT(zvec_query_params_diskann_get_list_size(diskann_params) == 500); err = zvec_query_params_diskann_set_list_size(diskann_params, 800); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(zvec_query_params_diskann_get_list_size(diskann_params) == 800); // Test DiskANN common parameters (radius, is_linear, is_using_refiner) err = zvec_query_params_diskann_set_radius(diskann_params, 1.2f); TEST_ASSERT(err == ZVEC_OK); radius = zvec_query_params_diskann_get_radius(diskann_params); TEST_ASSERT(radius == 1.2f); err = zvec_query_params_diskann_set_is_linear(diskann_params, true); TEST_ASSERT(err == ZVEC_OK); is_linear = zvec_query_params_diskann_get_is_linear(diskann_params); TEST_ASSERT(is_linear == true); err = zvec_query_params_diskann_set_is_using_refiner(diskann_params, true); TEST_ASSERT(err == ZVEC_OK); is_using_refiner = zvec_query_params_diskann_get_is_using_refiner(diskann_params); TEST_ASSERT(is_using_refiner == true); // Test destruction of valid parameters zvec_query_params_hnsw_destroy(hnsw_params); zvec_query_params_ivf_destroy(ivf_params); zvec_query_params_flat_destroy(flat_params); zvec_query_params_vamana_destroy(vamana_params); zvec_query_params_diskann_destroy(diskann_params); // Test boundary cases - null pointer handling zvec_query_params_hnsw_destroy(NULL); zvec_query_params_ivf_destroy(NULL); zvec_query_params_flat_destroy(NULL); zvec_query_params_vamana_destroy(NULL); zvec_query_params_diskann_destroy(NULL); // Test null pointer handling for setters err = zvec_query_params_hnsw_set_radius(NULL, 0.5f); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); err = zvec_query_params_ivf_set_radius(NULL, 0.5f); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); err = zvec_query_params_flat_set_radius(NULL, 0.5f); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); err = zvec_query_params_vamana_set_ef_search(NULL, 100); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); err = zvec_query_params_diskann_set_radius(NULL, 0.5f); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); err = zvec_query_params_diskann_set_list_size(NULL, 100); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); // Test default values for getters with NULL TEST_ASSERT(zvec_query_params_hnsw_get_radius(NULL) == 0.0f); TEST_ASSERT(zvec_query_params_ivf_get_radius(NULL) == 0.0f); TEST_ASSERT(zvec_query_params_flat_get_radius(NULL) == 0.0f); TEST_ASSERT(zvec_query_params_diskann_get_radius(NULL) == 0.0f); TEST_ASSERT(zvec_query_params_hnsw_get_is_linear(NULL) == false); TEST_ASSERT(zvec_query_params_ivf_get_is_linear(NULL) == false); TEST_ASSERT(zvec_query_params_flat_get_is_linear(NULL) == false); TEST_ASSERT(zvec_query_params_diskann_get_is_linear(NULL) == false); TEST_ASSERT(zvec_query_params_hnsw_get_is_using_refiner(NULL) == false); TEST_ASSERT(zvec_query_params_ivf_get_is_using_refiner(NULL) == false); TEST_ASSERT(zvec_query_params_flat_get_is_using_refiner(NULL) == false); TEST_ASSERT(zvec_query_params_vamana_get_ef_search(NULL) == 200); TEST_ASSERT(zvec_query_params_vamana_get_radius(NULL) == 0.0f); TEST_ASSERT(zvec_query_params_vamana_get_is_linear(NULL) == false); TEST_ASSERT(zvec_query_params_vamana_get_is_using_refiner(NULL) == false); TEST_ASSERT(zvec_query_params_diskann_get_is_using_refiner(NULL) == false); TEST_ASSERT(zvec_query_params_diskann_get_list_size(NULL) == 300); TEST_END(); } void test_collection_stats_functions(void) { TEST_START(); char temp_dir[] = "./zvec_test_collection_stats_functions"; zvec_collection_schema_t *schema = zvec_test_create_temp_schema(); TEST_ASSERT(schema != NULL); if (schema) { zvec_collection_t *collection = NULL; zvec_error_code_t err = zvec_collection_create_and_open(temp_dir, schema, NULL, &collection); TEST_ASSERT(err == ZVEC_OK); if (collection) { zvec_collection_stats_t *stats = NULL; // Test normal statistics retrieval err = zvec_collection_get_stats(collection, &stats); TEST_ASSERT(err == ZVEC_OK); if (stats) { TEST_ASSERT(zvec_collection_stats_get_doc_count(stats) == 0); zvec_collection_stats_destroy(stats); } // Test NULL parameters err = zvec_collection_get_stats(NULL, &stats); TEST_ASSERT(err != ZVEC_OK); err = zvec_collection_get_stats(collection, NULL); TEST_ASSERT(err != ZVEC_OK); // Test statistics destruction boundary cases zvec_collection_stats_destroy(NULL); zvec_collection_destroy(collection); } zvec_collection_schema_destroy(schema); } // Clean up temporary directory cleanup_temp_directory(temp_dir); TEST_END(); } void test_collection_dml_functions(void) { TEST_START(); char temp_dir[] = "./zvec_test_collection_dml"; zvec_collection_schema_t *schema = zvec_test_create_temp_schema(); TEST_ASSERT(schema != NULL); if (schema) { zvec_collection_t *collection = NULL; zvec_error_code_t err = zvec_collection_create_and_open(temp_dir, schema, NULL, &collection); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(collection != NULL); if (collection) { // Test insertion function boundary cases size_t success_count, error_count; // Test NULL collection err = zvec_collection_insert(NULL, NULL, 0, &success_count, &error_count); TEST_ASSERT(err != ZVEC_OK); // Test NULL document array err = zvec_collection_insert(collection, NULL, 0, &success_count, &error_count); TEST_ASSERT(err != ZVEC_OK); // Test zero document count zvec_doc_t *empty_docs[1]; err = zvec_collection_insert(collection, (const zvec_doc_t **)empty_docs, 0, &success_count, &error_count); TEST_ASSERT(err != ZVEC_OK); // Test NULL count pointer err = zvec_collection_insert(collection, (const zvec_doc_t **)empty_docs, 1, NULL, &error_count); TEST_ASSERT(err != ZVEC_OK); // Test update function boundary cases err = zvec_collection_update(NULL, NULL, 0, &success_count, &error_count); TEST_ASSERT(err != ZVEC_OK); err = zvec_collection_update(collection, NULL, 0, &success_count, &error_count); TEST_ASSERT(err != ZVEC_OK); err = zvec_collection_update(collection, (const zvec_doc_t **)empty_docs, 0, NULL, &error_count); TEST_ASSERT(err != ZVEC_OK); // Test upsert function boundary cases err = zvec_collection_upsert(NULL, NULL, 0, &success_count, &error_count); TEST_ASSERT(err != ZVEC_OK); err = zvec_collection_upsert(collection, NULL, 0, &success_count, &error_count); TEST_ASSERT(err != ZVEC_OK); err = zvec_collection_upsert(collection, (const zvec_doc_t **)empty_docs, 0, NULL, &error_count); TEST_ASSERT(err != ZVEC_OK); // Test deletion function boundary cases const char *pks[1]; err = zvec_collection_delete(NULL, NULL, 0, &success_count, &error_count); TEST_ASSERT(err != ZVEC_OK); err = zvec_collection_delete(collection, NULL, 0, &success_count, &error_count); TEST_ASSERT(err != ZVEC_OK); err = zvec_collection_delete(collection, pks, 0, NULL, &error_count); TEST_ASSERT(err != ZVEC_OK); // Test deletion by filter boundary cases err = zvec_collection_delete_by_filter(NULL, NULL); TEST_ASSERT(err != ZVEC_OK); err = zvec_collection_delete_by_filter(collection, NULL); TEST_ASSERT(err != ZVEC_OK); // Test detailed DML result APIs zvec_doc_t *result_doc = zvec_test_create_doc(101, schema, NULL); TEST_ASSERT(result_doc != NULL); if (result_doc) { zvec_doc_t *result_docs[] = {result_doc}; zvec_write_result_t *results = NULL; size_t result_count = 0; err = zvec_collection_upsert_with_results( collection, (const zvec_doc_t **)result_docs, 1, &results, &result_count); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(result_count == 1); if (results && result_count == 1) { TEST_ASSERT(results[0].code == ZVEC_OK); zvec_write_results_free(results, result_count); } const char *delete_pks[] = {"pk_101"}; results = NULL; result_count = 0; err = zvec_collection_delete_with_results(collection, delete_pks, 1, &results, &result_count); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(result_count == 1); if (results && result_count == 1) { zvec_write_results_free(results, result_count); } zvec_doc_destroy(result_doc); } zvec_collection_destroy(collection); } zvec_collection_schema_destroy(schema); } // Clean up temporary directory cleanup_temp_directory(temp_dir); TEST_END(); } void test_collection_nullable_roundtrip(void) { TEST_START(); char temp_dir[] = "./zvec_test_collection_nullable_roundtrip"; zvec_test_delete_dir(temp_dir); zvec_collection_schema_t *schema = zvec_test_create_temp_schema(); TEST_ASSERT(schema != NULL); if (!schema) { TEST_END(); return; } zvec_collection_t *collection = NULL; zvec_error_code_t err = zvec_collection_create_and_open(temp_dir, schema, NULL, &collection); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(collection != NULL); if (collection) { zvec_doc_t *doc = zvec_doc_create(); TEST_ASSERT(doc != NULL); if (doc) { zvec_doc_set_pk(doc, "pk_nullable"); int64_t id = 77; err = zvec_doc_add_field_by_value(doc, "id", ZVEC_DATA_TYPE_INT64, &id, sizeof(id)); TEST_ASSERT(err == ZVEC_OK); const char *name = "nullable"; err = zvec_doc_add_field_by_value(doc, "name", ZVEC_DATA_TYPE_STRING, name, strlen(name)); TEST_ASSERT(err == ZVEC_OK); // "weight" in temp schema is nullable. err = zvec_doc_set_field_null(doc, "weight"); TEST_ASSERT(err == ZVEC_OK); float dense[128]; for (size_t i = 0; i < 128; ++i) { dense[i] = (float)i / 128.0f; } err = zvec_doc_add_field_by_value( doc, "dense", ZVEC_DATA_TYPE_VECTOR_FP32, dense, sizeof(dense)); TEST_ASSERT(err == ZVEC_OK); uint32_t nnz = 3; uint32_t sparse_indices[] = {1, 5, 9}; float sparse_values[] = {0.2f, 0.5f, 0.9f}; char sparse_buffer[sizeof(nnz) + sizeof(sparse_indices) + sizeof(sparse_values)]; memcpy(sparse_buffer, &nnz, sizeof(nnz)); memcpy(sparse_buffer + sizeof(nnz), sparse_indices, sizeof(sparse_indices)); memcpy(sparse_buffer + sizeof(nnz) + sizeof(sparse_indices), sparse_values, sizeof(sparse_values)); err = zvec_doc_add_field_by_value(doc, "sparse", ZVEC_DATA_TYPE_SPARSE_VECTOR_FP32, sparse_buffer, sizeof(sparse_buffer)); TEST_ASSERT(err == ZVEC_OK); zvec_doc_t *docs[] = {doc}; size_t success_count = 0; size_t error_count = 0; err = zvec_collection_upsert(collection, (const zvec_doc_t **)docs, 1, &success_count, &error_count); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(success_count == 1); TEST_ASSERT(error_count == 0); const char *pks[] = {"pk_nullable"}; zvec_doc_t **fetched = NULL; size_t fetched_count = 0; err = zvec_collection_fetch(collection, pks, 1, NULL, 0, false, &fetched, &fetched_count); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(fetched_count == 1); if (fetched && fetched_count == 1) { TEST_ASSERT(zvec_doc_has_field(fetched[0], "weight") == true); TEST_ASSERT(zvec_doc_has_field_value(fetched[0], "weight") == false); TEST_ASSERT(zvec_doc_is_field_null(fetched[0], "weight") == true); } zvec_docs_free(fetched, fetched_count); zvec_doc_destroy(doc); } zvec_collection_destroy(collection); } zvec_collection_schema_destroy(schema); zvec_test_delete_dir(temp_dir); TEST_END(); } // ============================================================================= // Actual Query Execution Tests // ============================================================================= void test_actual_vector_queries(void) { TEST_START(); char temp_dir[] = "./zvec_test_actual_queries"; // Create schema with vector field zvec_collection_schema_t *schema = zvec_collection_schema_create("query_test"); TEST_ASSERT(schema != NULL); if (schema) { // Add ID field zvec_field_schema_t *id_field = zvec_field_schema_create("id", ZVEC_DATA_TYPE_INT64, false, 0); zvec_collection_schema_add_field(schema, id_field); // Add vector field with HNSW index zvec_index_params_t *hnsw_params = zvec_index_params_create(ZVEC_INDEX_TYPE_HNSW); TEST_ASSERT(hnsw_params != NULL); zvec_index_params_set_metric_type(hnsw_params, ZVEC_METRIC_TYPE_L2); zvec_index_params_set_hnsw_params(hnsw_params, 16, 100); zvec_field_schema_t *vec_field = zvec_field_schema_create( "embedding", ZVEC_DATA_TYPE_VECTOR_FP32, false, 4); zvec_field_schema_set_index_params(vec_field, hnsw_params); zvec_collection_schema_add_field(schema, vec_field); zvec_index_params_destroy(hnsw_params); zvec_collection_t *collection = NULL; zvec_error_code_t err = zvec_collection_create_and_open(temp_dir, schema, NULL, &collection); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(collection != NULL); if (collection) { // Insert test documents float vec1[] = {1.0f, 0.0f, 0.0f, 0.0f}; float vec2[] = {0.0f, 1.0f, 0.0f, 0.0f}; float vec3[] = {0.0f, 0.0f, 1.0f, 0.0f}; float vec4[] = {0.7f, 0.7f, 0.0f, 0.0f}; // Similar to vec1 and vec2 zvec_doc_t *docs[4]; for (int i = 0; i < 4; i++) { docs[i] = zvec_doc_create(); zvec_doc_set_pk(docs[i], zvec_test_make_pk(i + 1)); zvec_doc_add_field_by_value(docs[i], "id", ZVEC_DATA_TYPE_INT64, &(int64_t){i + 1}, sizeof(int64_t)); } zvec_doc_add_field_by_value( docs[0], "embedding", ZVEC_DATA_TYPE_VECTOR_FP32, vec1, sizeof(vec1)); zvec_doc_add_field_by_value( docs[1], "embedding", ZVEC_DATA_TYPE_VECTOR_FP32, vec2, sizeof(vec2)); zvec_doc_add_field_by_value( docs[2], "embedding", ZVEC_DATA_TYPE_VECTOR_FP32, vec3, sizeof(vec3)); zvec_doc_add_field_by_value( docs[3], "embedding", ZVEC_DATA_TYPE_VECTOR_FP32, vec4, sizeof(vec4)); size_t success_count, error_count; err = zvec_collection_insert(collection, (const zvec_doc_t **)docs, 4, &success_count, &error_count); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(success_count == 4); TEST_ASSERT(error_count == 0); // Flush collection to build index zvec_collection_flush(collection); // Test 1: Basic vector search zvec_vector_query_t *query1 = zvec_vector_query_create(); TEST_ASSERT(query1 != NULL); zvec_vector_query_set_field_name(query1, "embedding"); zvec_vector_query_set_query_vector(query1, vec1, sizeof(vec1)); zvec_vector_query_set_topk(query1, 3); zvec_vector_query_set_include_vector(query1, true); zvec_vector_query_set_include_doc_id(query1, true); zvec_doc_t **results = NULL; size_t result_count = 0; err = zvec_collection_query(collection, query1, &results, &result_count); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(result_count > 0); TEST_ASSERT(results != NULL); // First result should be vec1 itself (distance ~0) if (result_count > 0) { float score = zvec_doc_get_score(results[0]); TEST_ASSERT(score < 0.001f); // Very small distance } zvec_docs_free(results, result_count); // Test 2: Search with filter zvec_vector_query_set_filter(query1, "id > 2"); err = zvec_collection_query(collection, query1, &results, &result_count); TEST_ASSERT(err == ZVEC_OK); // Should only return documents with id > 2 for (size_t i = 0; i < result_count; i++) { int64_t id; zvec_doc_get_field_value_basic(results[i], "id", ZVEC_DATA_TYPE_INT64, &id, sizeof(id)); TEST_ASSERT(id > 2); } zvec_docs_free(results, result_count); // Cleanup documents and query for (int i = 0; i < 4; i++) { zvec_doc_destroy(docs[i]); } zvec_vector_query_destroy(query1); zvec_collection_destroy(collection); } zvec_collection_schema_destroy(schema); } // Clean up cleanup_temp_directory(temp_dir); TEST_END(); } // ============================================================================= // FTS (full-text search) tests // ============================================================================= void test_fts_index_params_functions(void) { TEST_START(); // Defaults: tokenizer="standard", filters=["lowercase"], extra_params="". zvec_index_params_t *params = zvec_index_params_create(ZVEC_INDEX_TYPE_FTS); TEST_ASSERT(params != NULL); TEST_ASSERT(zvec_index_params_get_type(params) == ZVEC_INDEX_TYPE_FTS); const char *tokenizer = NULL; const char *extra = NULL; zvec_string_array_t *filters = NULL; zvec_error_code_t err = zvec_index_params_get_fts_params(params, &tokenizer, &filters, &extra); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(tokenizer != NULL && strcmp(tokenizer, "standard") == 0); TEST_ASSERT(extra != NULL && strcmp(extra, "") == 0); TEST_ASSERT(filters != NULL && filters->count == 1); TEST_ASSERT(strcmp(filters->strings[0].data, "lowercase") == 0); zvec_string_array_destroy(filters); filters = NULL; // Override via set; filters list of 2 + extra_params + tokenizer. zvec_string_array_t *new_filters = zvec_string_array_create(2); TEST_ASSERT(new_filters != NULL); zvec_string_array_add(new_filters, 0, "lowercase"); zvec_string_array_add(new_filters, 1, "stop"); err = zvec_index_params_set_fts_params(params, "jieba", new_filters, "key=value"); TEST_ASSERT(err == ZVEC_OK); zvec_string_array_destroy(new_filters); err = zvec_index_params_get_fts_params(params, &tokenizer, &filters, &extra); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(tokenizer != NULL && strcmp(tokenizer, "jieba") == 0); TEST_ASSERT(extra != NULL && strcmp(extra, "key=value") == 0); TEST_ASSERT(filters != NULL && filters->count == 2); TEST_ASSERT(strcmp(filters->strings[0].data, "lowercase") == 0); TEST_ASSERT(strcmp(filters->strings[1].data, "stop") == 0); zvec_string_array_destroy(filters); // Type-mismatch error path: invert params must not accept fts setter. zvec_index_params_t *invert = zvec_index_params_create(ZVEC_INDEX_TYPE_INVERT); TEST_ASSERT(invert != NULL); err = zvec_index_params_set_fts_params(invert, "standard", NULL, ""); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); zvec_index_params_destroy(invert); // index_type_to_string should report FTS. const char *type_str = zvec_index_type_to_string(ZVEC_INDEX_TYPE_FTS); TEST_ASSERT(type_str != NULL && strcmp(type_str, "FTS") == 0); zvec_index_params_destroy(params); TEST_END(); } void test_fts_query_params_functions(void) { TEST_START(); // Empty default_operator → engine default (empty string). zvec_fts_query_params_t *p0 = zvec_query_params_fts_create(NULL); TEST_ASSERT(p0 != NULL); const char *op0 = zvec_query_params_fts_get_default_operator(p0); TEST_ASSERT(op0 != NULL && strcmp(op0, "") == 0); zvec_query_params_fts_destroy(p0); // Explicit AND. zvec_fts_query_params_t *p1 = zvec_query_params_fts_create("AND"); TEST_ASSERT(p1 != NULL); const char *op1 = zvec_query_params_fts_get_default_operator(p1); TEST_ASSERT(op1 != NULL && strcmp(op1, "AND") == 0); zvec_error_code_t err = zvec_query_params_fts_set_default_operator(p1, "OR"); TEST_ASSERT(err == ZVEC_OK); const char *op2 = zvec_query_params_fts_get_default_operator(p1); TEST_ASSERT(op2 != NULL && strcmp(op2, "OR") == 0); // NULL → invalid arg. err = zvec_query_params_fts_set_default_operator(NULL, "AND"); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); zvec_query_params_fts_destroy(p1); TEST_END(); } void test_fts_wiring_on_vector_query(void) { TEST_START(); zvec_fts_t *fts = zvec_fts_create(); TEST_ASSERT(fts != NULL); TEST_ASSERT(strcmp(zvec_fts_get_query_string(fts), "") == 0); TEST_ASSERT(strcmp(zvec_fts_get_match_string(fts), "") == 0); zvec_error_code_t err = zvec_fts_set_query_string(fts, "+hello -world \"phrase\""); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT( strcmp(zvec_fts_get_query_string(fts), "+hello -world \"phrase\"") == 0); err = zvec_fts_set_match_string(fts, "machine learning"); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(strcmp(zvec_fts_get_match_string(fts), "machine learning") == 0); zvec_vector_query_t *query = zvec_vector_query_create(); TEST_ASSERT(query != NULL); TEST_ASSERT(zvec_vector_query_get_fts(query) == NULL); err = zvec_vector_query_set_fts(query, fts); TEST_ASSERT(err == ZVEC_OK); const zvec_fts_t *got = zvec_vector_query_get_fts(query); TEST_ASSERT(got != NULL); TEST_ASSERT( strcmp(zvec_fts_get_query_string(got), "+hello -world \"phrase\"") == 0); TEST_ASSERT(strcmp(zvec_fts_get_match_string(got), "machine learning") == 0); // Setter copies the payload — mutating the original must not affect the // attached one. zvec_fts_set_query_string(fts, "changed"); TEST_ASSERT( strcmp(zvec_fts_get_query_string(zvec_vector_query_get_fts(query)), "+hello -world \"phrase\"") == 0); // Clearing. err = zvec_vector_query_set_fts(query, NULL); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(zvec_vector_query_get_fts(query) == NULL); // Attach FtsQueryParams (transfers ownership). zvec_fts_query_params_t *fts_params = zvec_query_params_fts_create("AND"); TEST_ASSERT(fts_params != NULL); err = zvec_vector_query_set_fts_params(query, fts_params); TEST_ASSERT(err == ZVEC_OK); // Ownership transferred — do NOT call zvec_query_params_fts_destroy on it. zvec_vector_query_destroy(query); zvec_fts_destroy(fts); TEST_END(); } void test_fts_wiring_on_sub_query(void) { TEST_START(); zvec_fts_t *fts = zvec_fts_create(); TEST_ASSERT(fts != NULL); zvec_error_code_t err = zvec_fts_set_query_string(fts, "+hello -world \"phrase\""); TEST_ASSERT(err == ZVEC_OK); err = zvec_fts_set_match_string(fts, "machine learning"); TEST_ASSERT(err == ZVEC_OK); zvec_sub_query_t *sq = zvec_sub_query_create(); TEST_ASSERT(sq != NULL); // Set FTS clause. err = zvec_sub_query_set_fts(sq, fts); TEST_ASSERT(err == ZVEC_OK); // Clearing. err = zvec_sub_query_set_fts(sq, NULL); TEST_ASSERT(err == ZVEC_OK); // Attach FtsQueryParams (transfers ownership). zvec_fts_query_params_t *fts_params = zvec_query_params_fts_create("AND"); TEST_ASSERT(fts_params != NULL); err = zvec_sub_query_set_fts_params(sq, fts_params); TEST_ASSERT(err == ZVEC_OK); // NULL argument checks. TEST_ASSERT(zvec_sub_query_set_fts(NULL, fts) == ZVEC_ERROR_INVALID_ARGUMENT); TEST_ASSERT(zvec_sub_query_set_fts_params(NULL, NULL) == ZVEC_ERROR_INVALID_ARGUMENT); zvec_sub_query_destroy(sq); zvec_fts_destroy(fts); TEST_END(); } void test_fts_end_to_end(void) { TEST_START(); char temp_dir[] = "./zvec_test_fts_end_to_end"; cleanup_temp_directory(temp_dir); zvec_collection_schema_t *schema = zvec_collection_schema_create("fts_e2e"); TEST_ASSERT(schema != NULL); if (!schema) { TEST_END(); return; } // id (int64) — primary scalar zvec_field_schema_t *id_field = zvec_field_schema_create("id", ZVEC_DATA_TYPE_INT64, false, 0); zvec_collection_schema_add_field(schema, id_field); // content (string) — FTS-indexed field, no vector field in the schema. zvec_index_params_t *fts_params = zvec_index_params_create(ZVEC_INDEX_TYPE_FTS); TEST_ASSERT(fts_params != NULL); zvec_field_schema_t *content_field = zvec_field_schema_create("content", ZVEC_DATA_TYPE_STRING, false, 0); zvec_field_schema_set_index_params(content_field, fts_params); zvec_collection_schema_add_field(schema, content_field); zvec_index_params_destroy(fts_params); zvec_collection_t *collection = NULL; zvec_error_code_t err = zvec_collection_create_and_open(temp_dir, schema, NULL, &collection); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(collection != NULL); if (collection) { const char *texts[3] = { "machine learning is fun", "deep learning uses neural networks", "vector databases store embeddings", }; zvec_doc_t *docs[3]; for (int i = 0; i < 3; i++) { docs[i] = zvec_doc_create(); zvec_doc_set_pk(docs[i], zvec_test_make_pk(i + 1)); int64_t id = i + 1; zvec_doc_add_field_by_value(docs[i], "id", ZVEC_DATA_TYPE_INT64, &id, sizeof(id)); zvec_doc_add_field_by_value(docs[i], "content", ZVEC_DATA_TYPE_STRING, texts[i], strlen(texts[i])); } size_t success_count = 0, error_count = 0; err = zvec_collection_insert(collection, (const zvec_doc_t **)docs, 3, &success_count, &error_count); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(success_count == 3); TEST_ASSERT(error_count == 0); zvec_collection_flush(collection); // FTS-only query (no query vector): match on "learning" should hit docs // 1+2. zvec_vector_query_t *query = zvec_vector_query_create(); TEST_ASSERT(query != NULL); zvec_vector_query_set_field_name(query, "content"); zvec_vector_query_set_topk(query, 10); zvec_vector_query_set_include_doc_id(query, true); zvec_fts_t *fts = zvec_fts_create(); zvec_fts_set_match_string(fts, "learning"); err = zvec_vector_query_set_fts(query, fts); TEST_ASSERT(err == ZVEC_OK); zvec_fts_destroy(fts); zvec_doc_t **results = NULL; size_t result_count = 0; err = zvec_collection_query(collection, query, &results, &result_count); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(result_count >= 2); zvec_docs_free(results, result_count); zvec_vector_query_destroy(query); for (int i = 0; i < 3; i++) { zvec_doc_destroy(docs[i]); } zvec_collection_destroy(collection); } zvec_collection_schema_destroy(schema); cleanup_temp_directory(temp_dir); TEST_END(); } // ==================== Multi-query reranker test helpers ==================== typedef struct { zvec_collection_t *collection; zvec_collection_schema_t *schema; zvec_doc_t *docs[4]; float e1_v1[4], e2_v1[4]; char temp_dir[64]; } multi_query_fixture_t; static int setup_multi_query_fixture(multi_query_fixture_t *f, const char *dir_name, const char *schema_name) { snprintf(f->temp_dir, sizeof(f->temp_dir), "./%s", dir_name); f->collection = NULL; f->schema = zvec_collection_schema_create(schema_name); if (!f->schema) return 0; zvec_field_schema_t *id_field = zvec_field_schema_create("id", ZVEC_DATA_TYPE_INT64, false, 0); zvec_collection_schema_add_field(f->schema, id_field); for (int i = 0; i < 2; i++) { const char *name = i == 0 ? "embedding1" : "embedding2"; zvec_index_params_t *hnsw = zvec_index_params_create(ZVEC_INDEX_TYPE_HNSW); zvec_index_params_set_metric_type(hnsw, ZVEC_METRIC_TYPE_L2); zvec_index_params_set_hnsw_params(hnsw, 16, 100); zvec_field_schema_t *vec = zvec_field_schema_create(name, ZVEC_DATA_TYPE_VECTOR_FP32, false, 4); zvec_field_schema_set_index_params(vec, hnsw); zvec_collection_schema_add_field(f->schema, vec); zvec_index_params_destroy(hnsw); } zvec_error_code_t err = zvec_collection_create_and_open( f->temp_dir, f->schema, NULL, &f->collection); if (err != ZVEC_OK || !f->collection) return 0; float e1[4][4] = {{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {.7f, .7f, 0, 0}}; float e2[4][4] = {{0, 1, 0, 0}, {1, 0, 0, 0}, {0, 0, 0, 1}, {.5f, .5f, 0, 0}}; memcpy(f->e1_v1, e1[0], sizeof(f->e1_v1)); memcpy(f->e2_v1, e2[0], sizeof(f->e2_v1)); for (int i = 0; i < 4; i++) { f->docs[i] = zvec_doc_create(); zvec_doc_set_pk(f->docs[i], zvec_test_make_pk(i + 1)); zvec_doc_add_field_by_value(f->docs[i], "id", ZVEC_DATA_TYPE_INT64, &(int64_t){i + 1}, sizeof(int64_t)); zvec_doc_add_field_by_value(f->docs[i], "embedding1", ZVEC_DATA_TYPE_VECTOR_FP32, e1[i], sizeof(e1[i])); zvec_doc_add_field_by_value(f->docs[i], "embedding2", ZVEC_DATA_TYPE_VECTOR_FP32, e2[i], sizeof(e2[i])); } size_t success_count, error_count; err = zvec_collection_insert(f->collection, (const zvec_doc_t **)f->docs, 4, &success_count, &error_count); if (err != ZVEC_OK || success_count != 4) return 0; zvec_collection_flush(f->collection); return 1; } static void teardown_multi_query_fixture(multi_query_fixture_t *f) { for (int i = 0; i < 4; i++) zvec_doc_destroy(f->docs[i]); zvec_collection_destroy(f->collection); zvec_collection_schema_destroy(f->schema); cleanup_temp_directory(f->temp_dir); } typedef enum { MQ_RERANK_RRF, MQ_RERANK_WEIGHTED, } mq_rerank_kind_t; static int execute_multi_query_with_rerank( const multi_query_fixture_t *f, mq_rerank_kind_t kind, int rank_constant, const double *weights, size_t weight_count, int topk, int num_candidates) { zvec_multi_query_t *mvq = zvec_multi_query_create(); if (!mvq) return -1; zvec_multi_query_set_topk(mvq, topk); zvec_multi_query_set_include_vector(mvq, false); zvec_sub_query_t *vq1 = zvec_sub_query_create(); zvec_sub_query_set_field_name(vq1, "embedding1"); zvec_sub_query_set_query_vector(vq1, f->e1_v1, sizeof(f->e1_v1)); zvec_sub_query_set_num_candidates(vq1, num_candidates); zvec_multi_query_add_sub_query(mvq, vq1); zvec_sub_query_t *vq2 = zvec_sub_query_create(); zvec_sub_query_set_field_name(vq2, "embedding2"); zvec_sub_query_set_query_vector(vq2, f->e2_v1, sizeof(f->e2_v1)); zvec_sub_query_set_num_candidates(vq2, num_candidates); zvec_multi_query_add_sub_query(mvq, vq2); if (kind == MQ_RERANK_WEIGHTED) { zvec_multi_query_set_rerank_weighted(mvq, weights, weight_count); } else { zvec_multi_query_set_rerank_rrf(mvq, rank_constant); } zvec_doc_t **results = NULL; size_t result_count = 0; zvec_error_code_t err = zvec_collection_multi_query(f->collection, mvq, &results, &result_count); int ret = -1; if (err == ZVEC_OK && results != NULL) { ret = (int)result_count; zvec_docs_free(results, result_count); } zvec_sub_query_destroy(vq1); zvec_sub_query_destroy(vq2); zvec_multi_query_destroy(mvq); return ret; } // ==================== Multi-query reranker tests ==================== void test_multi_vector_query_with_rrf_reranker(void) { TEST_START(); multi_query_fixture_t f; TEST_ASSERT(setup_multi_query_fixture(&f, "zvec_test_mq_rrf", "mq_rrf")); int count = execute_multi_query_with_rerank(&f, MQ_RERANK_RRF, 60, NULL, 0, 3, 3); TEST_ASSERT(count > 0); TEST_ASSERT(count <= 3); // MultiQuery property setters/getters zvec_multi_query_t *mvq2 = zvec_multi_query_create(); TEST_ASSERT(mvq2 != NULL); zvec_multi_query_set_topk(mvq2, 5); TEST_ASSERT(zvec_multi_query_get_topk(mvq2) == 5); zvec_multi_query_set_filter(mvq2, "id > 1"); TEST_ASSERT(strcmp(zvec_multi_query_get_filter(mvq2), "id > 1") == 0); zvec_multi_query_set_include_vector(mvq2, true); TEST_ASSERT(zvec_multi_query_get_include_vector(mvq2) == true); const char *out_fields[] = {"id"}; zvec_multi_query_set_output_fields(mvq2, out_fields, 1); const char **got_fields = NULL; size_t field_count = 0; zvec_error_code_t err = zvec_multi_query_get_output_fields(mvq2, &got_fields, &field_count); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(field_count == 1); if (field_count > 0) { TEST_ASSERT(strcmp(got_fields[0], "id") == 0); zvec_free((char *)got_fields); } zvec_sub_query_t *sparse_query = zvec_sub_query_create(); TEST_ASSERT(sparse_query != NULL); uint32_t sparse_indices[] = {1, 3}; float sparse_values[] = {0.25f, 0.75f}; err = zvec_sub_query_set_sparse_vector(sparse_query, sparse_indices, sparse_values, 2); TEST_ASSERT(err == ZVEC_OK); err = zvec_sub_query_set_sparse_vector(sparse_query, NULL, sparse_values, 2); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); err = zvec_sub_query_set_sparse_vector(sparse_query, sparse_indices, NULL, 2); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); zvec_sub_query_destroy(sparse_query); zvec_multi_query_destroy(mvq2); teardown_multi_query_fixture(&f); TEST_END(); } void test_multi_vector_query_with_weighted_reranker(void) { TEST_START(); multi_query_fixture_t f; TEST_ASSERT( setup_multi_query_fixture(&f, "zvec_test_mq_weighted", "mq_weighted")); double weights[] = {0.7, 0.3}; int count = execute_multi_query_with_rerank(&f, MQ_RERANK_WEIGHTED, 0, weights, 2, 3, 3); TEST_ASSERT(count > 0); TEST_ASSERT(count <= 3); teardown_multi_query_fixture(&f); TEST_END(); } void test_index_creation_and_management(void) { TEST_START(); char temp_dir[] = "./zvec_test_index_management"; zvec_collection_schema_t *schema = zvec_test_create_temp_schema(); TEST_ASSERT(schema != NULL); if (schema) { zvec_collection_t *collection = NULL; zvec_error_code_t err = zvec_collection_create_and_open(temp_dir, schema, NULL, &collection); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(collection != NULL); if (collection) { // Test 1: Create HNSW index zvec_index_params_t *hnsw_params = zvec_index_params_create(ZVEC_INDEX_TYPE_HNSW); TEST_ASSERT(hnsw_params != NULL); zvec_index_params_set_metric_type(hnsw_params, ZVEC_METRIC_TYPE_COSINE); zvec_index_params_set_hnsw_params(hnsw_params, 16, 100); err = zvec_collection_create_index(collection, "dense", hnsw_params); TEST_ASSERT(err == ZVEC_OK); // Test 2: Create scalar index zvec_index_params_t *invert_params = zvec_index_params_create(ZVEC_INDEX_TYPE_INVERT); TEST_ASSERT(invert_params != NULL); zvec_index_params_set_invert_params(invert_params, true, false); err = zvec_collection_create_index(collection, "name", invert_params); TEST_ASSERT(err == ZVEC_OK); err = zvec_collection_drop_index(collection, "name"); TEST_ASSERT(err == ZVEC_OK); // Test 3: Optimize collection err = zvec_collection_optimize(collection); TEST_ASSERT(err == ZVEC_OK); zvec_collection_destroy(collection); zvec_index_params_destroy(hnsw_params); zvec_index_params_destroy(invert_params); } zvec_collection_schema_destroy(schema); } // Clean up cleanup_temp_directory(temp_dir); TEST_END(); } void test_collection_ddl_operations(void) { TEST_START(); char temp_dir[] = "./zvec_test_collection_ddl"; zvec_collection_schema_t *schema = zvec_test_create_temp_schema(); TEST_ASSERT(schema != NULL); size_t field_count = get_field_count(schema); if (schema) { zvec_collection_t *collection = NULL; zvec_error_code_t err = zvec_collection_create_and_open(temp_dir, schema, NULL, &collection); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(collection != NULL); if (collection) { // Test 1: Add new column zvec_field_schema_t *new_field = zvec_field_schema_create("new_int32", ZVEC_DATA_TYPE_INT32, true, 0); TEST_ASSERT(new_field != NULL); err = zvec_collection_add_column(collection, new_field, NULL); TEST_ASSERT(err == ZVEC_OK); // Test 2: Get collection schema and verify field count zvec_collection_schema_t *retrieved_schema = NULL; err = zvec_collection_get_schema(collection, &retrieved_schema); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(retrieved_schema != NULL); size_t new_field_count = get_field_count(retrieved_schema); TEST_ASSERT((field_count + 1) == new_field_count); // Test 3: Alter column zvec_field_schema_t *alter_field = zvec_field_schema_create("new_float", ZVEC_DATA_TYPE_FLOAT, true, 0); TEST_ASSERT(alter_field != NULL); err = zvec_collection_alter_column(collection, "new_int32", "", alter_field); TEST_ASSERT(err == ZVEC_OK); // Test 4: Drop column err = zvec_collection_drop_column(collection, "new_float"); TEST_ASSERT(err == ZVEC_OK); // Test 5: Verify field count after drop err = zvec_collection_get_schema(collection, &retrieved_schema); TEST_ASSERT(err == ZVEC_OK); new_field_count = get_field_count(retrieved_schema); TEST_ASSERT(new_field_count == field_count); zvec_collection_schema_destroy(retrieved_schema); zvec_field_schema_destroy(new_field); zvec_field_schema_destroy(alter_field); zvec_collection_destroy(collection); } zvec_collection_schema_destroy(schema); } // Clean up cleanup_temp_directory(temp_dir); TEST_END(); } void test_field_ddl_operations(void) { TEST_START(); // Test field schema creation with various configurations zvec_field_schema_t *field1 = zvec_field_schema_create("test_field1", ZVEC_DATA_TYPE_STRING, false, 0); TEST_ASSERT(field1 != NULL); TEST_ASSERT(strcmp(zvec_field_schema_get_name(field1), "test_field1") == 0); TEST_ASSERT(zvec_field_schema_get_data_type(field1) == ZVEC_DATA_TYPE_STRING); TEST_ASSERT(zvec_field_schema_is_nullable(field1) == false); TEST_ASSERT(zvec_field_schema_get_dimension(field1) == 0); zvec_field_schema_t *field2 = zvec_field_schema_create( "test_field2", ZVEC_DATA_TYPE_VECTOR_FP32, true, 128); TEST_ASSERT(field2 != NULL); TEST_ASSERT(zvec_field_schema_get_data_type(field2) == ZVEC_DATA_TYPE_VECTOR_FP32); TEST_ASSERT(zvec_field_schema_is_nullable(field2) == true); TEST_ASSERT(zvec_field_schema_get_dimension(field2) == 128); // Test index parameter setting zvec_index_params_t *hnsw_params = zvec_index_params_create(ZVEC_INDEX_TYPE_HNSW); TEST_ASSERT(hnsw_params != NULL); zvec_index_params_set_metric_type(hnsw_params, ZVEC_METRIC_TYPE_L2); zvec_index_params_set_hnsw_params(hnsw_params, 16, 100); zvec_error_code_t err = zvec_field_schema_set_index_params(field2, hnsw_params); TEST_ASSERT(err == ZVEC_OK); // Cleanup zvec_field_schema_destroy(field1); zvec_field_schema_destroy(field2); zvec_index_params_destroy(hnsw_params); TEST_END(); } void test_performance_benchmarks(void) { TEST_START(); char temp_dir[] = "./zvec_test_performance"; zvec_collection_schema_t *schema = zvec_collection_schema_create("perf_test"); TEST_ASSERT(schema != NULL); if (schema) { // Create simple schema for performance testing zvec_field_schema_t *id_field = zvec_field_schema_create("id", ZVEC_DATA_TYPE_INT64, false, 0); zvec_collection_schema_add_field(schema, id_field); zvec_field_schema_t *vec_field = zvec_field_schema_create("vec", ZVEC_DATA_TYPE_VECTOR_FP32, false, 128); zvec_index_params_t *hnsw_params = zvec_index_params_create(ZVEC_INDEX_TYPE_HNSW); zvec_index_params_set_metric_type(hnsw_params, ZVEC_METRIC_TYPE_L2); zvec_index_params_set_hnsw_params(hnsw_params, 16, 100); zvec_field_schema_set_index_params(vec_field, hnsw_params); zvec_collection_schema_add_field(schema, vec_field); zvec_collection_t *collection = NULL; zvec_error_code_t err = zvec_collection_create_and_open(temp_dir, schema, NULL, &collection); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(collection != NULL); if (collection) { const size_t BATCH_SIZE = 1000; const size_t TOTAL_DOCS = 10000; // Test bulk insertion performance #ifdef _WIN32 clock_t start_clock = clock(); #else struct timeval start_time, end_time; gettimeofday(&start_time, NULL); #endif for (size_t batch_start = 0; batch_start < TOTAL_DOCS; batch_start += BATCH_SIZE) { // Use dynamic allocation for MSVC compatibility (no VLA support) zvec_doc_t **batch_docs = (zvec_doc_t **)malloc(BATCH_SIZE * sizeof(zvec_doc_t *)); if (!batch_docs) { fprintf(stderr, "Failed to allocate batch documents\n"); break; } size_t current_batch_size = (batch_start + BATCH_SIZE > TOTAL_DOCS) ? TOTAL_DOCS - batch_start : BATCH_SIZE; // Create batch of documents for (size_t i = 0; i < current_batch_size; i++) { batch_docs[i] = zvec_doc_create(); zvec_doc_set_pk(batch_docs[i], zvec_test_make_pk(batch_start + i)); int64_t id = batch_start + i; zvec_doc_add_field_by_value(batch_docs[i], "id", ZVEC_DATA_TYPE_INT64, &id, sizeof(id)); // Create random vector float vec[128]; for (int j = 0; j < 128; j++) { vec[j] = (float)rand() / RAND_MAX; } zvec_doc_add_field_by_value(batch_docs[i], "vec", ZVEC_DATA_TYPE_VECTOR_FP32, vec, sizeof(vec)); } // Insert batch size_t success_count, error_count; err = zvec_collection_insert( collection, (const zvec_doc_t **)batch_docs, current_batch_size, &success_count, &error_count); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(success_count == current_batch_size); TEST_ASSERT(error_count == 0); // Cleanup batch documents for (size_t i = 0; i < current_batch_size; i++) { zvec_doc_destroy(batch_docs[i]); } free(batch_docs); // Free the array itself } #ifdef _WIN32 clock_t end_clock = clock(); double insert_time = ((double)(end_clock - start_clock)) / CLOCKS_PER_SEC; #else gettimeofday(&end_time, NULL); double insert_time = (end_time.tv_sec - start_time.tv_sec) + (end_time.tv_usec - start_time.tv_usec) / 1000000.0; #endif printf(" Inserted %zu documents in %.3f seconds (%.0f docs/sec)\n", TOTAL_DOCS, insert_time, TOTAL_DOCS / insert_time); // Flush and optimize zvec_collection_flush(collection); zvec_collection_optimize(collection); // Test query performance float query_vec[128]; for (int i = 0; i < 128; i++) { query_vec[i] = (float)rand() / RAND_MAX; } zvec_vector_query_t *query = zvec_vector_query_create(); TEST_ASSERT(query != NULL); zvec_vector_query_set_field_name(query, "vec"); zvec_vector_query_set_query_vector(query, query_vec, sizeof(query_vec)); zvec_vector_query_set_topk(query, 10); zvec_vector_query_set_include_vector(query, false); zvec_vector_query_set_include_doc_id(query, true); const int QUERY_COUNT = 100; #ifdef _WIN32 clock_t query_start_clock = clock(); #else struct timeval query_start_time, query_end_time; gettimeofday(&query_start_time, NULL); #endif for (int q = 0; q < QUERY_COUNT; q++) { zvec_doc_t **results = NULL; size_t result_count = 0; err = zvec_collection_query(collection, query, &results, &result_count); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(result_count <= 10); zvec_docs_free(results, result_count); } #ifdef _WIN32 clock_t query_end_clock = clock(); double query_time = ((double)(query_end_clock - query_start_clock)) / CLOCKS_PER_SEC; #else gettimeofday(&query_end_time, NULL); double query_time = (query_end_time.tv_sec - query_start_time.tv_sec) + (query_end_time.tv_usec - query_start_time.tv_usec) / 1000000.0; #endif double avg_query_time = (query_time * 1000) / QUERY_COUNT; // ms per query printf(" Average query time: %.2f ms\n", avg_query_time); zvec_vector_query_destroy(query); zvec_collection_destroy(collection); } zvec_collection_schema_destroy(schema); } // Clean up cleanup_temp_directory(temp_dir); TEST_END(); } // ============================================================================= // Additional tests for uncovered API functions // ============================================================================= void test_zvec_shutdown(void) { TEST_START(); // Test shutdown zvec_error_code_t err = zvec_shutdown(); TEST_ASSERT(err == ZVEC_OK); // Re-initialize for other tests zvec_config_data_t *config = zvec_config_data_create(); TEST_ASSERT(config != NULL); err = zvec_initialize(config); TEST_ASSERT(err == ZVEC_OK); zvec_config_data_destroy(config); TEST_END(); } void test_index_params_creation_functions(void) { TEST_START(); // Test HNSW parameters using new API zvec_index_params_t *hnsw_params = zvec_index_params_create(ZVEC_INDEX_TYPE_HNSW); TEST_ASSERT(hnsw_params != NULL); TEST_ASSERT(zvec_index_params_get_type(hnsw_params) == ZVEC_INDEX_TYPE_HNSW); // Default metric type is L2 TEST_ASSERT(zvec_index_params_get_metric_type(hnsw_params) == ZVEC_METRIC_TYPE_L2); int m, ef_construction; zvec_index_params_set_metric_type(hnsw_params, ZVEC_METRIC_TYPE_COSINE); zvec_index_params_set_hnsw_params(hnsw_params, 16, 100); m = zvec_index_params_get_hnsw_m(hnsw_params); ef_construction = zvec_index_params_get_hnsw_ef_construction(hnsw_params); TEST_ASSERT(m == 16); TEST_ASSERT(ef_construction == 100); // Test IVF parameters using new API zvec_index_params_t *ivf_params = zvec_index_params_create(ZVEC_INDEX_TYPE_IVF); TEST_ASSERT(ivf_params != NULL); TEST_ASSERT(zvec_index_params_get_type(ivf_params) == ZVEC_INDEX_TYPE_IVF); TEST_ASSERT(zvec_index_params_get_metric_type(ivf_params) == ZVEC_METRIC_TYPE_L2); int n_list, n_iters; bool use_soar; zvec_index_params_set_ivf_params(ivf_params, 100, 10, true); zvec_index_params_get_ivf_params(ivf_params, &n_list, &n_iters, &use_soar); TEST_ASSERT(n_list == 100); TEST_ASSERT(n_iters == 10); TEST_ASSERT(use_soar == true); // Test Flat parameters using new API zvec_index_params_t *flat_params = zvec_index_params_create(ZVEC_INDEX_TYPE_FLAT); TEST_ASSERT(flat_params != NULL); TEST_ASSERT(zvec_index_params_get_type(flat_params) == ZVEC_INDEX_TYPE_FLAT); zvec_index_params_set_metric_type(flat_params, ZVEC_METRIC_TYPE_IP); TEST_ASSERT(zvec_index_params_get_metric_type(flat_params) == ZVEC_METRIC_TYPE_IP); // Test Invert parameters using new API zvec_index_params_t *invert_params = zvec_index_params_create(ZVEC_INDEX_TYPE_INVERT); TEST_ASSERT(invert_params != NULL); TEST_ASSERT(zvec_index_params_get_type(invert_params) == ZVEC_INDEX_TYPE_INVERT); bool enable_range_opt, enable_wildcard; zvec_index_params_set_invert_params(invert_params, true, false); zvec_index_params_get_invert_params(invert_params, &enable_range_opt, &enable_wildcard); TEST_ASSERT(enable_range_opt == true); TEST_ASSERT(enable_wildcard == false); // Test Vamana parameters using new API zvec_index_params_t *vamana_params = zvec_index_params_create(ZVEC_INDEX_TYPE_VAMANA); TEST_ASSERT(vamana_params != NULL); TEST_ASSERT(zvec_index_params_get_type(vamana_params) == ZVEC_INDEX_TYPE_VAMANA); TEST_ASSERT(zvec_index_params_get_metric_type(vamana_params) == ZVEC_METRIC_TYPE_L2); int max_degree, search_list_size; float alpha; bool saturate_graph, use_contiguous_memory; zvec_error_code_t verr; // Set and get Vamana params verr = zvec_index_params_set_vamana_params(vamana_params, 128, 200, 1.5f, true, true); TEST_ASSERT(verr == ZVEC_OK); verr = zvec_index_params_get_vamana_params( vamana_params, &max_degree, &search_list_size, &alpha, &saturate_graph, &use_contiguous_memory); TEST_ASSERT(verr == ZVEC_OK); TEST_ASSERT(max_degree == 128); TEST_ASSERT(search_list_size == 200); TEST_ASSERT(alpha == 1.5f); TEST_ASSERT(saturate_graph == true); TEST_ASSERT(use_contiguous_memory == true); // Set metric and quantize type zvec_index_params_set_metric_type(vamana_params, ZVEC_METRIC_TYPE_COSINE); TEST_ASSERT(zvec_index_params_get_metric_type(vamana_params) == ZVEC_METRIC_TYPE_COSINE); // Test type mismatch: set_vamana_params on non-Vamana params should fail verr = zvec_index_params_set_vamana_params(hnsw_params, 64, 100, 1.2f, false, false); TEST_ASSERT(verr == ZVEC_ERROR_INVALID_ARGUMENT); // Test DiskANN parameters using new API zvec_index_params_t *diskann_params = zvec_index_params_create(ZVEC_INDEX_TYPE_DISKANN); TEST_ASSERT(diskann_params != NULL); TEST_ASSERT(zvec_index_params_get_type(diskann_params) == ZVEC_INDEX_TYPE_DISKANN); zvec_index_params_set_metric_type(diskann_params, ZVEC_METRIC_TYPE_COSINE); zvec_index_params_set_diskann_params(diskann_params, 64, 25, 4); TEST_ASSERT(zvec_index_params_get_diskann_max_degree(diskann_params) == 64); TEST_ASSERT(zvec_index_params_get_diskann_list_size(diskann_params) == 25); TEST_ASSERT(zvec_index_params_get_diskann_pq_chunk_num(diskann_params) == 4); // Cleanup zvec_index_params_destroy(hnsw_params); zvec_index_params_destroy(ivf_params); zvec_index_params_destroy(flat_params); zvec_index_params_destroy(invert_params); zvec_index_params_destroy(vamana_params); zvec_index_params_destroy(diskann_params); TEST_END(); } void test_collection_advanced_index_functions(void) { TEST_START(); const char *temp_dir = "./zvec_test_advanced_index"; zvec_test_delete_dir(temp_dir); // Create schema zvec_collection_schema_t *schema = zvec_collection_schema_create("test_collection"); TEST_ASSERT(schema != NULL); if (schema) { // Add fields zvec_field_schema_t *id_field = zvec_field_schema_create("id", ZVEC_DATA_TYPE_INT64, false, 0); zvec_field_schema_t *vec_field = zvec_field_schema_create("vec", ZVEC_DATA_TYPE_VECTOR_FP32, false, 128); zvec_collection_schema_add_field(schema, id_field); zvec_collection_schema_add_field(schema, vec_field); zvec_collection_options_t *options = zvec_collection_options_create(); TEST_ASSERT(options != NULL); zvec_collection_t *collection = NULL; zvec_error_code_t err = zvec_collection_create_and_open(temp_dir, schema, options, &collection); TEST_ASSERT(err == ZVEC_OK); if (collection) { // Test zvec_collection_create_index with FLAT type zvec_index_params_t *flat_params = zvec_index_params_create(ZVEC_INDEX_TYPE_FLAT); TEST_ASSERT(flat_params != NULL); zvec_index_params_set_metric_type(flat_params, ZVEC_METRIC_TYPE_L2); err = zvec_collection_create_index(collection, "vec", flat_params); TEST_ASSERT(err == ZVEC_OK); // Test zvec_collection_create_index with IVF type zvec_index_params_t *ivf_params = zvec_index_params_create(ZVEC_INDEX_TYPE_IVF); TEST_ASSERT(ivf_params != NULL); zvec_index_params_set_metric_type(ivf_params, ZVEC_METRIC_TYPE_L2); zvec_index_params_set_ivf_params(ivf_params, 100, 10, true); err = zvec_collection_drop_index(collection, "vec"); // Drop previous index first TEST_ASSERT(err == ZVEC_OK); err = zvec_collection_create_index(collection, "vec", ivf_params); TEST_ASSERT(err == ZVEC_OK); // Test zvec_collection_create_index with HNSW type zvec_index_params_t *hnsw_params = zvec_index_params_create(ZVEC_INDEX_TYPE_HNSW); TEST_ASSERT(hnsw_params != NULL); zvec_index_params_set_metric_type(hnsw_params, ZVEC_METRIC_TYPE_COSINE); zvec_index_params_set_hnsw_params(hnsw_params, 16, 100); err = zvec_collection_drop_index(collection, "vec"); // Drop previous index first TEST_ASSERT(err == ZVEC_OK); err = zvec_collection_create_index(collection, "vec", hnsw_params); TEST_ASSERT(err == ZVEC_OK); // Test zvec_field_schema_set_index_params zvec_field_schema_t *new_vec_field = zvec_field_schema_create( "vec2", ZVEC_DATA_TYPE_VECTOR_FP32, false, 128); TEST_ASSERT(new_vec_field != NULL); zvec_index_params_t *ivf_params2 = zvec_index_params_create(ZVEC_INDEX_TYPE_IVF); TEST_ASSERT(ivf_params2 != NULL); zvec_index_params_set_metric_type(ivf_params2, ZVEC_METRIC_TYPE_IP); zvec_index_params_set_ivf_params(ivf_params2, 50, 5, false); zvec_field_schema_set_index_params(new_vec_field, ivf_params2); TEST_ASSERT(zvec_field_schema_has_index(new_vec_field) == true); zvec_field_schema_destroy(new_vec_field); zvec_index_params_destroy(flat_params); zvec_index_params_destroy(ivf_params); zvec_index_params_destroy(hnsw_params); zvec_index_params_destroy(ivf_params2); zvec_collection_options_destroy(options); zvec_collection_destroy(collection); } zvec_collection_schema_destroy(schema); } zvec_test_delete_dir(temp_dir); TEST_END(); } void test_collection_query_functions(void) { TEST_START(); const char *temp_dir = "./zvec_test_query_funcs"; zvec_test_delete_dir(temp_dir); // Create schema and collection zvec_collection_schema_t *schema = zvec_collection_schema_create("query_test"); zvec_index_params_t *hnsw_params = zvec_index_params_create(ZVEC_INDEX_TYPE_HNSW); TEST_ASSERT(hnsw_params != NULL); zvec_index_params_set_metric_type(hnsw_params, ZVEC_METRIC_TYPE_L2); zvec_index_params_set_hnsw_params(hnsw_params, 16, 100); zvec_field_schema_t *name_field = zvec_field_schema_create("name", ZVEC_DATA_TYPE_STRING, false, 0); zvec_field_schema_t *vec_field = zvec_field_schema_create("vec", ZVEC_DATA_TYPE_VECTOR_FP32, false, 4); zvec_field_schema_set_index_params(vec_field, hnsw_params); zvec_index_params_destroy(hnsw_params); zvec_collection_schema_add_field(schema, name_field); zvec_collection_schema_add_field(schema, vec_field); zvec_collection_t *collection = NULL; zvec_error_code_t err = zvec_collection_create_and_open(temp_dir, schema, NULL, &collection); TEST_ASSERT(err == ZVEC_OK); if (collection) { // Insert test documents zvec_doc_t *doc1 = zvec_doc_create(); zvec_doc_set_pk(doc1, "doc1"); float vec1[4] = {1.0f, 0.0f, 0.0f, 0.0f}; zvec_doc_add_field_by_value(doc1, "vec", ZVEC_DATA_TYPE_VECTOR_FP32, vec1, sizeof(vec1)); zvec_doc_add_field_by_value(doc1, "name", ZVEC_DATA_TYPE_STRING, "document1", 9); zvec_doc_t *doc2 = zvec_doc_create(); zvec_doc_set_pk(doc2, "doc2"); float vec2[4] = {0.0f, 1.0f, 0.0f, 0.0f}; zvec_doc_add_field_by_value(doc2, "vec", ZVEC_DATA_TYPE_VECTOR_FP32, vec2, sizeof(vec2)); zvec_doc_add_field_by_value(doc2, "name", ZVEC_DATA_TYPE_STRING, "document2", 9); zvec_doc_t *docs[] = {doc1, doc2}; size_t success_count, error_count; err = zvec_collection_insert(collection, (const zvec_doc_t **)docs, 2, &success_count, &error_count); TEST_ASSERT(err == ZVEC_OK); zvec_collection_flush(collection); zvec_collection_optimize(collection); // Test zvec_collection_fetch (fetch all fields, NULL output_fields) const char *pks[] = {"doc1", "doc2"}; zvec_doc_t **results = NULL; size_t found_count = 0; err = zvec_collection_fetch(collection, pks, 2, NULL, 0, false, &results, &found_count); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(found_count == 2); if (results && found_count == 2) { // Both docs should have the "name" field TEST_ASSERT(zvec_doc_has_field(results[0], "name") == true || zvec_doc_has_field(results[1], "name") == true); } zvec_docs_free(results, found_count); // Test zvec_collection_fetch with output_fields=["name"] zvec_doc_t **results_partial = NULL; size_t found_count_partial = 0; const char *output_fields[] = {"name"}; err = zvec_collection_fetch(collection, pks, 2, output_fields, 1, false, &results_partial, &found_count_partial); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(found_count_partial == 2); if (results_partial && found_count_partial == 2) { for (size_t i = 0; i < found_count_partial; ++i) { TEST_ASSERT(zvec_doc_has_field(results_partial[i], "name") == true); } } zvec_docs_free(results_partial, found_count_partial); // Test zvec_collection_fetch with empty output_fields (no scalar fields) zvec_doc_t **results_empty_fields = NULL; size_t found_count_empty = 0; err = zvec_collection_fetch(collection, pks, 2, NULL, 0, false, &results_empty_fields, &found_count_empty); TEST_ASSERT(err == ZVEC_OK); zvec_docs_free(results_empty_fields, found_count_empty); // Test zvec_collection_fetch with include_vector=true zvec_doc_t **results_with_vec = NULL; size_t found_count_vec = 0; err = zvec_collection_fetch(collection, pks, 2, NULL, 0, true, &results_with_vec, &found_count_vec); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(found_count_vec == 2); zvec_docs_free(results_with_vec, found_count_vec); // Test zvec_collection_get_options zvec_collection_options_t *options = NULL; err = zvec_collection_get_options(collection, &options); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(options != NULL); zvec_collection_options_destroy(options); zvec_collection_destroy(collection); zvec_doc_destroy(doc1); zvec_doc_destroy(doc2); } zvec_collection_schema_destroy(schema); zvec_test_delete_dir(temp_dir); TEST_END(); } void test_doc_advanced_functions(void) { TEST_START(); // Test zvec_doc_clear zvec_doc_t *doc = zvec_doc_create(); zvec_doc_set_pk(doc, "test_pk"); zvec_doc_add_field_by_value(doc, "field1", ZVEC_DATA_TYPE_INT32, &(int32_t){100}, sizeof(int32_t)); TEST_ASSERT(zvec_doc_get_field_count(doc) > 0); zvec_doc_clear(doc); TEST_ASSERT(zvec_doc_get_field_count(doc) == 0); // Test zvec_doc_get_pk_copy zvec_doc_set_pk(doc, "test_pk_copy"); const char *pk_copy = zvec_doc_get_pk_copy(doc); TEST_ASSERT(pk_copy != NULL); TEST_ASSERT(strcmp(pk_copy, "test_pk_copy") == 0); zvec_free((void *)pk_copy); // Test zvec_doc_is_empty zvec_doc_t *empty_doc = zvec_doc_create(); TEST_ASSERT(zvec_doc_is_empty(empty_doc) == true); zvec_doc_add_field_by_value(empty_doc, "test", ZVEC_DATA_TYPE_INT32, &(int32_t){1}, sizeof(int32_t)); TEST_ASSERT(zvec_doc_is_empty(empty_doc) == false); zvec_doc_destroy(empty_doc); // Test zvec_doc_memory_usage zvec_doc_t *mem_doc = zvec_doc_create(); zvec_doc_set_pk(mem_doc, "memory_test"); char large_data[1024]; memset(large_data, 'A', sizeof(large_data)); zvec_doc_add_field_by_value(mem_doc, "large_field", ZVEC_DATA_TYPE_STRING, large_data, sizeof(large_data)); size_t mem_usage = zvec_doc_memory_usage(mem_doc); TEST_ASSERT(mem_usage > 0); zvec_doc_destroy(mem_doc); // Test zvec_doc_merge zvec_doc_t *doc1 = zvec_doc_create(); zvec_doc_set_pk(doc1, "merge_test"); zvec_doc_add_field_by_value(doc1, "field1", ZVEC_DATA_TYPE_INT32, &(int32_t){100}, sizeof(int32_t)); zvec_doc_t *doc2 = zvec_doc_create(); zvec_doc_add_field_by_value(doc2, "field2", ZVEC_DATA_TYPE_STRING, "merged", 6); zvec_doc_merge(doc1, doc2); TEST_ASSERT(zvec_doc_has_field(doc1, "field1") == true); TEST_ASSERT(zvec_doc_has_field(doc1, "field2") == true); zvec_doc_destroy(doc1); zvec_doc_destroy(doc2); // Test zvec_doc_validate zvec_collection_schema_t *schema = zvec_collection_schema_create("validate_test"); zvec_field_schema_t *val_field = zvec_field_schema_create("test_field", ZVEC_DATA_TYPE_INT32, false, 0); zvec_collection_schema_add_field(schema, val_field); zvec_doc_t *val_doc = zvec_doc_create(); zvec_doc_set_pk(val_doc, "test_pk"); zvec_doc_add_field_by_value(val_doc, "test_field", ZVEC_DATA_TYPE_INT32, &(int32_t){42}, sizeof(int32_t)); zvec_doc_destroy(val_doc); zvec_collection_schema_destroy(schema); zvec_doc_destroy(doc); // Test zvec_doc_to_detail_string zvec_doc_t *detail_doc = zvec_doc_create(); zvec_doc_set_pk(detail_doc, "detail_test"); zvec_doc_add_field_by_value(detail_doc, "int_field", ZVEC_DATA_TYPE_INT32, &(int32_t){12345}, sizeof(int32_t)); zvec_doc_add_field_by_value(detail_doc, "str_field", ZVEC_DATA_TYPE_STRING, "hello", 5); char *detail_str = NULL; zvec_error_code_t err = zvec_doc_to_detail_string(detail_doc, &detail_str); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(detail_str != NULL); // printf(" Document detail: %s\n", detail_str); zvec_free(detail_str); zvec_doc_destroy(detail_doc); TEST_END(); } void test_array_memory_functions(void) { TEST_START(); // Test zvec_string_array_t zvec_string_array_t *str_array = zvec_string_array_create(3); TEST_ASSERT(str_array != NULL); if (str_array) { TEST_ASSERT(str_array->count == 3); TEST_ASSERT(str_array->strings != NULL); // Add strings at specific indices zvec_string_array_add(str_array, 0, "string1"); zvec_string_array_add(str_array, 1, "string2"); zvec_string_array_add(str_array, 2, "string3"); // Verify strings were added TEST_ASSERT(strcmp(str_array->strings[0].data, "string1") == 0); TEST_ASSERT(strcmp(str_array->strings[1].data, "string2") == 0); TEST_ASSERT(strcmp(str_array->strings[2].data, "string3") == 0); zvec_string_array_destroy(str_array); } // Test zvec_mutable_byte_array_t zvec_mutable_byte_array_t *byte_array = zvec_byte_array_create(1024); TEST_ASSERT(byte_array != NULL); if (byte_array) { TEST_ASSERT(byte_array->capacity == 1024); TEST_ASSERT(byte_array->length == 0); TEST_ASSERT(byte_array->data != NULL); // Write some data byte_array->data[0] = 0x01; byte_array->data[1] = 0x02; byte_array->data[2] = 0x03; byte_array->length = 3; TEST_ASSERT(byte_array->length == 3); TEST_ASSERT(byte_array->data[0] == 0x01); TEST_ASSERT(byte_array->data[1] == 0x02); TEST_ASSERT(byte_array->data[2] == 0x03); zvec_byte_array_destroy(byte_array); } // Test zvec_float_array_t zvec_float_array_t *float_array = zvec_float_array_create(10); TEST_ASSERT(float_array != NULL); if (float_array) { TEST_ASSERT(float_array->length == 10); TEST_ASSERT(float_array->data != NULL); // Note: Data is initialized to 0 by zvec_float_array_create // The const qualifier indicates this is typically used for read-only access // For testing, we verify the allocation succeeded and length is correct TEST_ASSERT(float_array->data[0] == 0.0f); TEST_ASSERT(float_array->data[9] == 0.0f); zvec_float_array_destroy(float_array); } // Test zvec_int64_array_t zvec_int64_array_t *int64_array = zvec_int64_array_create(5); TEST_ASSERT(int64_array != NULL); if (int64_array) { TEST_ASSERT(int64_array->length == 5); TEST_ASSERT(int64_array->data != NULL); // Note: Data is initialized to 0 by zvec_int64_array_create // The const qualifier indicates this is typically used for read-only access TEST_ASSERT(int64_array->data[0] == 0); TEST_ASSERT(int64_array->data[4] == 0); zvec_int64_array_destroy(int64_array); } // Test edge case: create with zero size zvec_mutable_byte_array_t *zero_array = zvec_byte_array_create(0); TEST_ASSERT(zero_array != NULL); if (zero_array) { zvec_byte_array_destroy(zero_array); } TEST_END(); } // ============================================================================= // Missing API coverage tests // ============================================================================= void test_collection_open_close(void) { TEST_START(); const char *temp_dir = "./zvec_test_open_close"; zvec_test_delete_dir(temp_dir); // First create a collection zvec_collection_schema_t *schema = zvec_collection_schema_create("open_close_test"); TEST_ASSERT(schema != NULL); zvec_field_schema_t *id_field = zvec_field_schema_create("id", ZVEC_DATA_TYPE_INT32, false, 0); zvec_field_schema_t *vec_field = zvec_field_schema_create("vec", ZVEC_DATA_TYPE_VECTOR_FP32, false, 4); zvec_collection_schema_add_field(schema, id_field); zvec_collection_schema_add_field(schema, vec_field); zvec_collection_options_t *options = zvec_collection_options_create(); zvec_collection_t *collection = NULL; zvec_error_code_t err = zvec_collection_create_and_open(temp_dir, schema, options, &collection); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(collection != NULL); // Insert some data before closing if (collection) { zvec_doc_t *doc = zvec_doc_create(); zvec_doc_set_pk(doc, "doc1"); int32_t id_val = 1; float vec_data[] = {1.0f, 2.0f, 3.0f, 4.0f}; zvec_doc_add_field_by_value(doc, "id", ZVEC_DATA_TYPE_INT32, &id_val, sizeof(int32_t)); zvec_doc_add_field_by_value(doc, "vec", ZVEC_DATA_TYPE_VECTOR_FP32, vec_data, sizeof(vec_data)); const zvec_doc_t *docs[] = {doc}; size_t success_count = 0; size_t error_count = 0; err = zvec_collection_insert(collection, docs, 1, &success_count, &error_count); if (err != ZVEC_OK || success_count != 1) { // Print error for debugging char *error_msg = NULL; zvec_get_last_error(&error_msg); printf(" Insert error: %s (err=%d, success=%zu)\n", error_msg ? error_msg : "unknown", err, success_count); if (error_msg) zvec_free(error_msg); } TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(success_count == 1); zvec_doc_destroy(doc); // Close the collection err = zvec_collection_close(collection); TEST_ASSERT(err == ZVEC_OK); // Re-open the existing collection using zvec_collection_open zvec_collection_t *reopened_collection = NULL; err = zvec_collection_open(temp_dir, options, &reopened_collection); if (err != ZVEC_OK) { char *error_msg = NULL; zvec_get_last_error(&error_msg); printf(" Open error: %s (err=%d)\n", error_msg ? error_msg : "unknown", err); if (error_msg) zvec_free(error_msg); } TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(reopened_collection != NULL); // Verify data is still accessible after re-open if (reopened_collection) { // Use query API instead of filter zvec_vector_query_t *query = zvec_vector_query_create(); zvec_vector_query_set_topk(query, 10); zvec_vector_query_set_field_name(query, "vec"); float query_vec[] = {1.0f, 1.0f, 1.0f, 1.0f}; zvec_vector_query_set_query_vector(query, query_vec, sizeof(query_vec)); zvec_doc_t **results = NULL; size_t result_count = 0; err = zvec_collection_query(reopened_collection, query, &results, &result_count); if (err != ZVEC_OK || result_count < 1) { char *error_msg = NULL; zvec_get_last_error(&error_msg); printf(" Query error: %s (err=%d, count=%zu)\n", error_msg ? error_msg : "unknown", err, result_count); if (error_msg) zvec_free(error_msg); } TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(result_count >= 1); if (result_count > 0) { const char *pk = zvec_doc_get_pk_copy(results[0]); TEST_ASSERT(pk != NULL); TEST_ASSERT(strcmp(pk, "doc1") == 0); zvec_free((void *)pk); for (size_t i = 0; i < result_count; i++) { zvec_doc_destroy(results[i]); } zvec_free(results); } zvec_vector_query_destroy(query); } // Close the reopened collection if (reopened_collection) { err = zvec_collection_close(reopened_collection); TEST_ASSERT(err == ZVEC_OK); } } // Test NULL pointer error handling err = zvec_collection_open(NULL, options, &collection); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); err = zvec_collection_open(temp_dir, options, NULL); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); err = zvec_collection_close(NULL); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); zvec_collection_options_destroy(options); zvec_collection_schema_destroy(schema); zvec_test_delete_dir(temp_dir); TEST_END(); } void test_collection_options_getters(void) { TEST_START(); // Test default values zvec_collection_options_t *options = zvec_collection_options_create(); TEST_ASSERT(options != NULL); // Test enable_mmap getter bool enable_mmap = zvec_collection_options_get_enable_mmap(options); TEST_ASSERT(enable_mmap == true || enable_mmap == false); // Set and verify zvec_error_code_t err = zvec_collection_options_set_enable_mmap(options, false); TEST_ASSERT(err == ZVEC_OK); enable_mmap = zvec_collection_options_get_enable_mmap(options); TEST_ASSERT(enable_mmap == false); // Test max_buffer_size getter size_t max_buffer_size = zvec_collection_options_get_max_buffer_size(options); TEST_ASSERT(max_buffer_size > 0); // Should have a default value // Set and verify err = zvec_collection_options_set_max_buffer_size(options, 1024 * 1024); TEST_ASSERT(err == ZVEC_OK); max_buffer_size = zvec_collection_options_get_max_buffer_size(options); TEST_ASSERT(max_buffer_size == 1024 * 1024); // Test NULL pointer handling - these return defaults, not 0 TEST_ASSERT(zvec_collection_options_get_enable_mmap(NULL) == true); // Default is true TEST_ASSERT(zvec_collection_options_get_max_buffer_size(NULL) > 0); // Default is non-zero zvec_collection_options_destroy(options); TEST_END(); } void test_collection_stats_index_info(void) { TEST_START(); const char *temp_dir = "./zvec_test_stats_index"; zvec_test_delete_dir(temp_dir); zvec_collection_schema_t *schema = zvec_collection_schema_create("stats_index_test"); zvec_field_schema_t *vec_field = zvec_field_schema_create("vec", ZVEC_DATA_TYPE_VECTOR_FP32, false, 4); zvec_collection_schema_add_field(schema, vec_field); zvec_collection_t *collection = NULL; zvec_error_code_t err = zvec_collection_create_and_open(temp_dir, schema, NULL, &collection); TEST_ASSERT(err == ZVEC_OK); if (collection) { // Create an index first zvec_index_params_t *hnsw_params = zvec_index_params_create(ZVEC_INDEX_TYPE_HNSW); zvec_index_params_set_metric_type(hnsw_params, ZVEC_METRIC_TYPE_L2); zvec_index_params_set_hnsw_params(hnsw_params, 16, 100); err = zvec_collection_create_index(collection, "vec", hnsw_params); TEST_ASSERT(err == ZVEC_OK); zvec_index_params_destroy(hnsw_params); // Get collection stats zvec_collection_stats_t *stats = NULL; err = zvec_collection_get_stats(collection, &stats); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(stats != NULL); // Test index count size_t index_count = zvec_collection_stats_get_index_count(stats); TEST_ASSERT(index_count >= 1); // Should have at least one index // Test index name getter const char *index_name = zvec_collection_stats_get_index_name(stats, 0); TEST_ASSERT(index_name != NULL); // printf(" Index name at 0: %s\n", index_name); // Test index completeness getter float completeness = zvec_collection_stats_get_index_completeness(stats, 0); TEST_ASSERT(completeness >= 0.0f && completeness <= 1.0f); // printf(" Index completeness at 0: %.2f\n", completeness); // Test out-of-bounds access index_name = zvec_collection_stats_get_index_name(stats, 999); TEST_ASSERT(index_name == NULL); completeness = zvec_collection_stats_get_index_completeness(stats, 999); TEST_ASSERT(completeness == 0.0f); // Test NULL pointer handling TEST_ASSERT(zvec_collection_stats_get_index_name(NULL, 0) == NULL); TEST_ASSERT(zvec_collection_stats_get_index_completeness(NULL, 0) == 0.0f); zvec_collection_stats_destroy(stats); } zvec_collection_destroy(collection); zvec_collection_schema_destroy(schema); zvec_test_delete_dir(temp_dir); TEST_END(); } void test_field_schema_validate(void) { TEST_START(); // Test valid field schema zvec_field_schema_t *valid_field = zvec_field_schema_create("valid_field", ZVEC_DATA_TYPE_INT32, false, 0); TEST_ASSERT(valid_field != NULL); zvec_string_t *error_msg = NULL; zvec_error_code_t err = zvec_field_schema_validate(valid_field, &error_msg); TEST_ASSERT(err == ZVEC_OK); if (error_msg) { zvec_free_string(error_msg); } // Test field with index params zvec_field_schema_t *field_with_index = zvec_field_schema_create( "vec_field", ZVEC_DATA_TYPE_VECTOR_FP32, false, 128); zvec_index_params_t *index_params = zvec_index_params_create(ZVEC_INDEX_TYPE_HNSW); zvec_index_params_set_metric_type(index_params, ZVEC_METRIC_TYPE_L2); zvec_index_params_set_hnsw_params(index_params, 16, 100); zvec_field_schema_set_index_params(field_with_index, index_params); err = zvec_field_schema_validate(field_with_index, &error_msg); TEST_ASSERT(err == ZVEC_OK); if (error_msg) { zvec_free_string(error_msg); } // Test NULL schema pointer err = zvec_field_schema_validate(NULL, &error_msg); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); if (error_msg) { zvec_free_string(error_msg); } // Test with NULL error_msg (should not crash) err = zvec_field_schema_validate(valid_field, NULL); TEST_ASSERT(err == ZVEC_OK); zvec_index_params_destroy(index_params); zvec_field_schema_destroy(field_with_index); zvec_field_schema_destroy(valid_field); TEST_END(); } void test_doc_remove_field(void) { TEST_START(); zvec_doc_t *doc = zvec_doc_create(); TEST_ASSERT(doc != NULL); // Add some fields zvec_doc_set_pk(doc, "test_pk"); int32_t val1 = 100; int32_t val2 = 200; zvec_doc_add_field_by_value(doc, "field1", ZVEC_DATA_TYPE_INT32, &val1, sizeof(int32_t)); zvec_doc_add_field_by_value(doc, "field2", ZVEC_DATA_TYPE_INT32, &val2, sizeof(int32_t)); // Verify fields exist TEST_ASSERT(zvec_doc_has_field(doc, "field1") == true); TEST_ASSERT(zvec_doc_has_field(doc, "field2") == true); TEST_ASSERT(zvec_doc_get_field_count(doc) == 2); // Remove field1 zvec_error_code_t err = zvec_doc_remove_field(doc, "field1"); TEST_ASSERT(err == ZVEC_OK); // Verify field1 is removed TEST_ASSERT(zvec_doc_has_field(doc, "field1") == false); TEST_ASSERT(zvec_doc_has_field(doc, "field2") == true); TEST_ASSERT(zvec_doc_get_field_count(doc) == 1); // Try to remove non-existent field (should not crash) err = zvec_doc_remove_field(doc, "non_existent_field"); TEST_ASSERT(err == ZVEC_OK); // Or might return error, depending on design // Test NULL pointer handling err = zvec_doc_remove_field(NULL, "field"); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); err = zvec_doc_remove_field(doc, NULL); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); // Clean up zvec_doc_destroy(doc); TEST_END(); } void test_collection_schema_getters(void) { TEST_START(); zvec_collection_schema_t *schema = zvec_collection_schema_create("schema_getter_test"); TEST_ASSERT(schema != NULL); // Add multiple fields zvec_field_schema_t *field1 = zvec_field_schema_create("field1", ZVEC_DATA_TYPE_INT32, false, 0); zvec_field_schema_t *field2 = zvec_field_schema_create("field2", ZVEC_DATA_TYPE_STRING, true, 0); zvec_field_schema_t *field3 = zvec_field_schema_create( "field3", ZVEC_DATA_TYPE_VECTOR_FP32, false, 128); zvec_collection_schema_add_field(schema, field1); zvec_collection_schema_add_field(schema, field2); zvec_collection_schema_add_field(schema, field3); // Test has_field TEST_ASSERT(zvec_collection_schema_has_field(schema, "field1") == true); TEST_ASSERT(zvec_collection_schema_has_field(schema, "field2") == true); TEST_ASSERT(zvec_collection_schema_has_field(schema, "field3") == true); TEST_ASSERT(zvec_collection_schema_has_field(schema, "non_existent") == false); // Test get_field by name zvec_field_schema_t *retrieved_field = zvec_collection_schema_get_field(schema, "field2"); TEST_ASSERT(retrieved_field != NULL); TEST_ASSERT(zvec_field_schema_get_data_type(retrieved_field) == ZVEC_DATA_TYPE_STRING); // Test non-existent field name retrieved_field = zvec_collection_schema_get_field(schema, "non_existent"); TEST_ASSERT(retrieved_field == NULL); // Test get_forward_field (scalar fields only) retrieved_field = zvec_collection_schema_get_forward_field(schema, "field1"); TEST_ASSERT(retrieved_field != NULL); retrieved_field = zvec_collection_schema_get_forward_field(schema, "field3"); TEST_ASSERT(retrieved_field == NULL); // field3 is vector, not scalar // Test get_vector_field retrieved_field = zvec_collection_schema_get_vector_field(schema, "field3"); TEST_ASSERT(retrieved_field != NULL); TEST_ASSERT(zvec_field_schema_get_data_type(retrieved_field) == ZVEC_DATA_TYPE_VECTOR_FP32); retrieved_field = zvec_collection_schema_get_vector_field(schema, "field1"); TEST_ASSERT(retrieved_field == NULL); // field1 is scalar, not vector // Test get_all_field_names const char **field_names = NULL; size_t field_count = 0; zvec_error_code_t err = zvec_collection_schema_get_all_field_names( schema, &field_names, &field_count); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(field_names != NULL); TEST_ASSERT(field_count == 3); printf(" Field names: "); for (size_t i = 0; i < field_count; i++) { printf("%s ", field_names[i]); } printf("\n"); zvec_free(field_names); // Test get_forward_fields zvec_field_schema_t **forward_fields = NULL; size_t forward_count = 0; err = zvec_collection_schema_get_forward_fields(schema, &forward_fields, &forward_count); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(forward_fields != NULL); TEST_ASSERT(forward_count == 2); // field1 and field2 are scalars // Note: forward_fields contains pointers to fields owned by schema, // do not destroy them individually - just free the array zvec_free(forward_fields); // Test get_vector_fields zvec_field_schema_t **vector_fields = NULL; size_t vector_count = 0; err = zvec_collection_schema_get_vector_fields(schema, &vector_fields, &vector_count); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(vector_fields != NULL); TEST_ASSERT(vector_count == 1); // Only field3 is vector // Note: vector_fields contains pointers to fields owned by schema, // do not destroy them individually - just free the array zvec_free(vector_fields); // Test NULL pointer handling TEST_ASSERT(zvec_collection_schema_has_field(NULL, "field") == false); TEST_ASSERT(zvec_collection_schema_get_field(NULL, "field") == NULL); TEST_ASSERT(zvec_collection_schema_get_forward_field(NULL, "field") == NULL); TEST_ASSERT(zvec_collection_schema_get_vector_field(NULL, "field") == NULL); // Destroy schema - it owns all fields added to it zvec_collection_schema_destroy(schema); TEST_END(); } // ============================================================================= // DiskANN Tests // ============================================================================= void test_diskann_index_params_functions(void) { TEST_START(); // Create DiskANN index params with defaults zvec_index_params_t *params = zvec_index_params_create(ZVEC_INDEX_TYPE_DISKANN); TEST_ASSERT(params != NULL); TEST_ASSERT(zvec_index_params_get_type(params) == ZVEC_INDEX_TYPE_DISKANN); // Check defaults: max_degree=100, list_size=50, pq_chunk_num=0 // (aligned with DiskAnnIndexParams constructor defaults) TEST_ASSERT(zvec_index_params_get_diskann_max_degree(params) == 100); TEST_ASSERT(zvec_index_params_get_diskann_list_size(params) == 50); TEST_ASSERT(zvec_index_params_get_diskann_pq_chunk_num(params) == 0); // Default metric type is L2 TEST_ASSERT(zvec_index_params_get_metric_type(params) == ZVEC_METRIC_TYPE_L2); // Set and verify custom values zvec_index_params_set_metric_type(params, ZVEC_METRIC_TYPE_COSINE); TEST_ASSERT(zvec_index_params_get_metric_type(params) == ZVEC_METRIC_TYPE_COSINE); zvec_error_code_t err = zvec_index_params_set_diskann_params(params, 200, 100, 8); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(zvec_index_params_get_diskann_max_degree(params) == 200); TEST_ASSERT(zvec_index_params_get_diskann_list_size(params) == 100); TEST_ASSERT(zvec_index_params_get_diskann_pq_chunk_num(params) == 8); // Type-mismatch error path: HNSW params must not accept DiskANN setter zvec_index_params_t *hnsw = zvec_index_params_create(ZVEC_INDEX_TYPE_HNSW); TEST_ASSERT(hnsw != NULL); err = zvec_index_params_set_diskann_params(hnsw, 100, 50, 0); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); zvec_index_params_destroy(hnsw); // NULL pointer handling err = zvec_index_params_set_diskann_params(NULL, 100, 50, 0); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); TEST_ASSERT(zvec_index_params_get_diskann_max_degree(NULL) == 0); TEST_ASSERT(zvec_index_params_get_diskann_list_size(NULL) == 0); TEST_ASSERT(zvec_index_params_get_diskann_pq_chunk_num(NULL) == 0); // to_string should report DiskANN const char *type_str = zvec_index_type_to_string(ZVEC_INDEX_TYPE_DISKANN); TEST_ASSERT(type_str != NULL && strcmp(type_str, "DiskANN") == 0); zvec_index_params_destroy(params); TEST_END(); } void test_diskann_query_params_functions(void) { TEST_START(); // Create with default list_size zvec_diskann_query_params_t *p_default = zvec_query_params_diskann_create(300); TEST_ASSERT(p_default != NULL); TEST_ASSERT(zvec_query_params_diskann_get_list_size(p_default) == 300); zvec_query_params_diskann_destroy(p_default); // Create with custom list_size zvec_diskann_query_params_t *p = zvec_query_params_diskann_create(500); TEST_ASSERT(p != NULL); TEST_ASSERT(zvec_query_params_diskann_get_list_size(p) == 500); // Set/get list_size zvec_error_code_t err = zvec_query_params_diskann_set_list_size(p, 1000); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(zvec_query_params_diskann_get_list_size(p) == 1000); // Common params: radius err = zvec_query_params_diskann_set_radius(p, 1.5f); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(zvec_query_params_diskann_get_radius(p) == 1.5f); // Common params: is_linear err = zvec_query_params_diskann_set_is_linear(p, true); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(zvec_query_params_diskann_get_is_linear(p) == true); // Common params: is_using_refiner err = zvec_query_params_diskann_set_is_using_refiner(p, true); TEST_ASSERT(err == ZVEC_OK); TEST_ASSERT(zvec_query_params_diskann_get_is_using_refiner(p) == true); zvec_query_params_diskann_destroy(p); // NULL pointer handling: destroy zvec_query_params_diskann_destroy(NULL); // NULL pointer handling: setters return error err = zvec_query_params_diskann_set_list_size(NULL, 100); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); err = zvec_query_params_diskann_set_radius(NULL, 0.5f); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); err = zvec_query_params_diskann_set_is_linear(NULL, false); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); err = zvec_query_params_diskann_set_is_using_refiner(NULL, false); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); // NULL pointer handling: getters return safe defaults TEST_ASSERT(zvec_query_params_diskann_get_list_size(NULL) == 300); TEST_ASSERT(zvec_query_params_diskann_get_radius(NULL) == 0.0f); TEST_ASSERT(zvec_query_params_diskann_get_is_linear(NULL) == false); TEST_ASSERT(zvec_query_params_diskann_get_is_using_refiner(NULL) == false); TEST_END(); } void test_diskann_wiring_on_vector_query(void) { TEST_START(); zvec_error_code_t err; // Test wiring on zvec_vector_query_t zvec_vector_query_t *vq = zvec_vector_query_create(); TEST_ASSERT(vq != NULL); zvec_diskann_query_params_t *dp1 = zvec_query_params_diskann_create(400); TEST_ASSERT(dp1 != NULL); err = zvec_vector_query_set_diskann_params(vq, dp1); TEST_ASSERT(err == ZVEC_OK); // NULL handling zvec_diskann_query_params_t *dp_null = zvec_query_params_diskann_create(100); err = zvec_vector_query_set_diskann_params(NULL, dp_null); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); zvec_query_params_diskann_destroy(dp_null); err = zvec_vector_query_set_diskann_params(vq, NULL); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); zvec_vector_query_destroy(vq); // Test wiring on zvec_group_by_vector_query_t zvec_group_by_vector_query_t *gbq = zvec_group_by_vector_query_create(); TEST_ASSERT(gbq != NULL); zvec_diskann_query_params_t *dp2 = zvec_query_params_diskann_create(200); TEST_ASSERT(dp2 != NULL); err = zvec_group_by_vector_query_set_diskann_params(gbq, dp2); TEST_ASSERT(err == ZVEC_OK); err = zvec_group_by_vector_query_set_diskann_params(NULL, dp2); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); zvec_group_by_vector_query_destroy(gbq); // Test wiring on zvec_sub_query_t zvec_sub_query_t *sq = zvec_sub_query_create(); TEST_ASSERT(sq != NULL); zvec_diskann_query_params_t *dp3 = zvec_query_params_diskann_create(150); TEST_ASSERT(dp3 != NULL); err = zvec_sub_query_set_diskann_params(sq, dp3); TEST_ASSERT(err == ZVEC_OK); err = zvec_sub_query_set_diskann_params(NULL, dp3); TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT); zvec_sub_query_destroy(sq); TEST_END(); } // ============================================================================= // Main function // ============================================================================= int main(void) { printf("Starting comprehensive C API tests...\n\n"); // Clean up previous test directories printf("Cleaning up previous test directories...\n"); #ifdef _WIN32 system("rmdir /s /q %TEMP%\\zvec_test_* 2>nul"); system("del /q %TEMP%\\zvec_test_* 2>nul"); #else { glob_t gl; if (glob("/tmp/zvec_test_*", 0, NULL, &gl) == 0) { for (size_t gi = 0; gi < gl.gl_pathc; gi++) { zvec_test_delete_dir(gl.gl_pathv[gi]); } globfree(&gl); } } #endif printf("Cleanup completed.\n\n"); test_version_functions(); test_error_handling_functions(); test_zvec_config(); test_zvec_initialize(); test_zvec_string_functions(); // Schema-related tests test_schema_basic_operations(); test_schema_edge_cases(); test_schema_field_operations(); test_normal_schema_creation(); test_schema_with_indexes(); test_schema_max_doc_count(); test_collection_schema_helpers(); test_collection_schema_alter_field(); // Field-related tests test_field_schema_functions(); test_field_helper_functions(); test_field_ddl_operations(); // Collection-related tests test_collection_basic_operations(); test_collection_edge_cases(); test_collection_delete_by_filter(); test_collection_stats(); test_collection_stats_functions(); test_collection_dml_functions(); test_collection_nullable_roundtrip(); test_collection_ddl_operations(); // Doc-related tests test_doc_creation(); test_doc_primary_key(); test_doc_basic_operations(); test_doc_null_field_api(); test_doc_get_field_value_basic(); test_doc_get_field_value_copy(); test_doc_get_field_value_pointer(); test_doc_field_operations(); test_doc_error_conditions(); test_doc_serialization(); test_doc_add_field_by_value(); test_doc_add_field_by_struct(); // Index tests test_index_params(); test_index_params_functions(); test_quantizer_enable_rotate(); test_int8_rotate_e2e(); test_index_params_api_functions(); test_index_creation_and_management(); // Query tests test_query_params_functions(); test_actual_vector_queries(); // FTS tests test_fts_index_params_functions(); test_fts_query_params_functions(); test_fts_wiring_on_vector_query(); test_fts_wiring_on_sub_query(); test_fts_end_to_end(); // DiskANN tests test_diskann_index_params_functions(); test_diskann_query_params_functions(); test_diskann_wiring_on_vector_query(); test_multi_vector_query_with_rrf_reranker(); test_multi_vector_query_with_weighted_reranker(); // Performance tests // test_performance_benchmarks(); // Utility function tests test_utility_functions(); // Memory management tests test_memory_management_functions(); // Missing API coverage tests (before shutdown) test_collection_open_close(); test_collection_options_getters(); test_collection_stats_index_info(); test_field_schema_validate(); test_doc_remove_field(); test_collection_schema_getters(); // Additional API coverage tests test_zvec_shutdown(); test_index_params_creation_functions(); test_collection_advanced_index_functions(); test_collection_query_functions(); test_doc_advanced_functions(); test_array_memory_functions(); printf("\n=== Comprehensive Test Summary ===\n"); printf("Total tests: %d\n", test_count); printf("Passed: %d\n", passed_count); printf("Failed: %d\n", test_count - passed_count); return test_count == passed_count ? 0 : 1; }