You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
proxysql/test/tap/tests/genai_module-t.cpp

377 lines
12 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/**
* @file genai_module-t.cpp
* @brief TAP test for the GenAI module
*
* This test verifies the functionality of the GenAI (Generative AI) module in ProxySQL.
* It tests:
* - LOAD/SAVE commands for GenAI variables across all variants
* - Variable access (SET and SELECT) for genai-var1 and genai-var2
* - Variable persistence across storage layers (memory, disk, runtime)
* - CHECKSUM commands for GenAI variables
* - SHOW VARIABLES for GenAI module
*
* @date 2025-01-08
*/
#include <algorithm>
#include <string>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <vector>
#include <tuple>
#include "mysql.h"
#include "mysqld_error.h"
#include "tap.h"
#include "command_line.h"
#include "utils.h"
using std::string;
/**
* @brief Helper function to add LOAD/SAVE command variants for GenAI module
*
* This function generates all the standard LOAD/SAVE command variants that
* ProxySQL supports for module variables. It follows the same pattern used
* for other modules like MYSQL, PGSQL, etc.
*
* @param queries Vector to append the generated commands to
*/
void add_genai_load_save_commands(std::vector<std::string>& queries) {
// LOAD commands - Memory variants
queries.push_back("LOAD GENAI VARIABLES TO MEMORY");
queries.push_back("LOAD GENAI VARIABLES TO MEM");
// LOAD from disk
queries.push_back("LOAD GENAI VARIABLES FROM DISK");
// LOAD from memory
queries.push_back("LOAD GENAI VARIABLES FROM MEMORY");
queries.push_back("LOAD GENAI VARIABLES FROM MEM");
// LOAD to runtime
queries.push_back("LOAD GENAI VARIABLES TO RUNTIME");
queries.push_back("LOAD GENAI VARIABLES TO RUN");
// SAVE from memory
queries.push_back("SAVE GENAI VARIABLES FROM MEMORY");
queries.push_back("SAVE GENAI VARIABLES FROM MEM");
// SAVE to disk
queries.push_back("SAVE GENAI VARIABLES TO DISK");
// SAVE to memory
queries.push_back("SAVE GENAI VARIABLES TO MEMORY");
queries.push_back("SAVE GENAI VARIABLES TO MEM");
// SAVE from runtime
queries.push_back("SAVE GENAI VARIABLES FROM RUNTIME");
queries.push_back("SAVE GENAI VARIABLES FROM RUN");
}
/**
* @brief Get the value of a GenAI variable as a string
*
* @param admin MySQL connection to admin interface
* @param var_name Variable name (without genai- prefix)
* @return std::string The variable value, or empty string on error
*/
std::string get_genai_variable(MYSQL* admin, const std::string& var_name) {
std::string query = "SELECT @@genai-" + var_name;
if (mysql_query(admin, query.c_str()) != 0) {
return "";
}
MYSQL_RES* res = mysql_store_result(admin);
if (!res) {
return "";
}
MYSQL_ROW row = mysql_fetch_row(res);
std::string value = row && row[0] ? row[0] : "";
mysql_free_result(res);
return value;
}
/**
* @brief Test variable access operations (SET and SELECT)
*
* Tests setting and retrieving GenAI variables to ensure they work correctly.
*/
int test_variable_access(MYSQL* admin) {
int test_num = 0;
// Test 1: Get default value of genai-var1
std::string var1_default = get_genai_variable(admin, "var1");
ok(var1_default == "default_value_1",
"Default value of genai-var1 is 'default_value_1', got '%s'", var1_default.c_str());
// Test 2: Get default value of genai-var2
std::string var2_default = get_genai_variable(admin, "var2");
ok(var2_default == "100",
"Default value of genai-var2 is '100', got '%s'", var2_default.c_str());
// Test 3: Set genai-var1 to a new value
MYSQL_QUERY(admin, "SET genai-var1='test_value_123'");
std::string var1_new = get_genai_variable(admin, "var1");
ok(var1_new == "test_value_123",
"After SET, genai-var1 is 'test_value_123', got '%s'", var1_new.c_str());
// Test 4: Set genai-var2 to a new integer value
MYSQL_QUERY(admin, "SET genai-var2=42");
std::string var2_new = get_genai_variable(admin, "var2");
ok(var2_new == "42",
"After SET, genai-var2 is '42', got '%s'", var2_new.c_str());
// Test 5: Set genai-var1 with special characters
MYSQL_QUERY(admin, "SET genai-var1='test with spaces'");
std::string var1_special = get_genai_variable(admin, "var1");
ok(var1_special == "test with spaces",
"genai-var1 with spaces is 'test with spaces', got '%s'", var1_special.c_str());
// Test 6: Verify SHOW VARIABLES LIKE pattern
MYSQL_QUERY(admin, "SHOW VARIABLES LIKE 'genai-%'");
MYSQL_RES* res = mysql_store_result(admin);
int num_rows = mysql_num_rows(res);
ok(num_rows == 2,
"SHOW VARIABLES LIKE 'genai-%%' returns 2 rows, got %d", num_rows);
mysql_free_result(res);
// Test 7: Restore default values
MYSQL_QUERY(admin, "SET genai-var1='default_value_1'");
MYSQL_QUERY(admin, "SET genai-var2=100");
ok(1, "Restored default values for genai-var1 and genai-var2");
return test_num;
}
/**
* @brief Test variable persistence across storage layers
*
* Tests that variables are correctly copied between:
* - Memory (main.global_variables)
* - Disk (disk.global_variables)
* - Runtime (GloGATH handler object)
*/
int test_variable_persistence(MYSQL* admin) {
int test_num = 0;
// Test 1: Set values and save to disk
diag("Testing variable persistence: Set values, save to disk, modify, load from disk");
MYSQL_QUERY(admin, "SET genai-var1='disk_test_value'");
MYSQL_QUERY(admin, "SET genai-var2=999");
MYSQL_QUERY(admin, "SAVE GENAI VARIABLES TO DISK");
ok(1, "Set genai-var1='disk_test_value', genai-var2=999 and saved to disk");
// Test 2: Modify values in memory
MYSQL_QUERY(admin, "SET genai-var1='memory_value'");
MYSQL_QUERY(admin, "SET genai-var2=111");
std::string var1_mem = get_genai_variable(admin, "var1");
std::string var2_mem = get_genai_variable(admin, "var2");
ok(var1_mem == "memory_value" && var2_mem == "111",
"Modified in memory: genai-var1='memory_value', genai-var2='111'");
// Test 3: Load from disk and verify original values restored
MYSQL_QUERY(admin, "LOAD GENAI VARIABLES FROM DISK");
std::string var1_disk = get_genai_variable(admin, "var1");
std::string var2_disk = get_genai_variable(admin, "var2");
ok(var1_disk == "disk_test_value" && var2_disk == "999",
"After LOAD FROM DISK: genai-var1='disk_test_value', genai-var2='999'");
// Test 4: Save to memory and verify
MYSQL_QUERY(admin, "SAVE GENAI VARIABLES TO MEMORY");
ok(1, "SAVE GENAI VARIABLES TO MEMORY executed");
// Test 5: Load from memory
MYSQL_QUERY(admin, "LOAD GENAI VARIABLES FROM MEMORY");
ok(1, "LOAD GENAI VARIABLES FROM MEMORY executed");
// Test 6: Test SAVE from runtime
MYSQL_QUERY(admin, "SAVE GENAI VARIABLES FROM RUNTIME");
ok(1, "SAVE GENAI VARIABLES FROM RUNTIME executed");
// Test 7: Test LOAD to runtime
MYSQL_QUERY(admin, "LOAD GENAI VARIABLES TO RUNTIME");
ok(1, "LOAD GENAI VARIABLES TO RUNTIME executed");
// Test 8: Restore default values
MYSQL_QUERY(admin, "SET genai-var1='default_value_1'");
MYSQL_QUERY(admin, "SET genai-var2=100");
MYSQL_QUERY(admin, "SAVE GENAI VARIABLES TO DISK");
ok(1, "Restored default values and saved to disk");
return test_num;
}
/**
* @brief Test CHECKSUM commands for GenAI variables
*
* Tests all CHECKSUM variants to ensure they work correctly.
*/
int test_checksum_commands(MYSQL* admin) {
int test_num = 0;
// Test 1: CHECKSUM DISK GENAI VARIABLES
diag("Testing CHECKSUM commands for GenAI variables");
int rc1 = mysql_query(admin, "CHECKSUM DISK GENAI VARIABLES");
ok(rc1 == 0, "CHECKSUM DISK GENAI VARIABLES");
if (rc1 == 0) {
MYSQL_RES* res = mysql_store_result(admin);
int num_rows = mysql_num_rows(res);
ok(num_rows == 1, "CHECKSUM DISK GENAI VARIABLES returns 1 row");
mysql_free_result(res);
} else {
skip(1, "Skipping row count check due to error");
}
// Test 2: CHECKSUM MEM GENAI VARIABLES
int rc2 = mysql_query(admin, "CHECKSUM MEM GENAI VARIABLES");
ok(rc2 == 0, "CHECKSUM MEM GENAI VARIABLES");
if (rc2 == 0) {
MYSQL_RES* res = mysql_store_result(admin);
int num_rows = mysql_num_rows(res);
ok(num_rows == 1, "CHECKSUM MEM GENAI VARIABLES returns 1 row");
mysql_free_result(res);
} else {
skip(1, "Skipping row count check due to error");
}
// Test 3: CHECKSUM MEMORY GENAI VARIABLES (alias for MEM)
int rc3 = mysql_query(admin, "CHECKSUM MEMORY GENAI VARIABLES");
ok(rc3 == 0, "CHECKSUM MEMORY GENAI VARIABLES");
if (rc3 == 0) {
MYSQL_RES* res = mysql_store_result(admin);
int num_rows = mysql_num_rows(res);
ok(num_rows == 1, "CHECKSUM MEMORY GENAI VARIABLES returns 1 row");
mysql_free_result(res);
} else {
skip(1, "Skipping row count check due to error");
}
// Test 4: CHECKSUM GENAI VARIABLES (defaults to DISK)
int rc4 = mysql_query(admin, "CHECKSUM GENAI VARIABLES");
ok(rc4 == 0, "CHECKSUM GENAI VARIABLES");
if (rc4 == 0) {
MYSQL_RES* res = mysql_store_result(admin);
int num_rows = mysql_num_rows(res);
ok(num_rows == 1, "CHECKSUM GENAI VARIABLES returns 1 row");
mysql_free_result(res);
} else {
skip(1, "Skipping row count check due to error");
}
return test_num;
}
/**
* @brief Main test function
*
* Orchestrates all GenAI module tests.
*/
int main() {
CommandLine cl;
if (cl.getEnv()) {
diag("Failed to get the required environmental variables.");
return EXIT_FAILURE;
}
// Initialize connection to admin interface
MYSQL* admin = mysql_init(NULL);
if (!admin) {
fprintf(stderr, "File %s, line %d, Error: mysql_init failed\n", __FILE__, __LINE__);
return EXIT_FAILURE;
}
if (!mysql_real_connect(admin, cl.host, cl.admin_username, cl.admin_password,
NULL, cl.admin_port, NULL, 0)) {
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(admin));
return EXIT_FAILURE;
}
diag("Connected to ProxySQL admin interface at %s:%d", cl.host, cl.admin_port);
// Build the list of LOAD/SAVE commands to test
std::vector<std::string> queries;
add_genai_load_save_commands(queries);
// Each command test = 2 tests (execution + optional result check)
// LOAD/SAVE commands: 14 commands
// Variable access tests: 7 tests
// Persistence tests: 8 tests
// CHECKSUM tests: 8 tests (4 commands × 2)
int num_load_save_tests = (int)queries.size() * 2; // Each command + result check
int total_tests = num_load_save_tests + 7 + 8 + 8;
plan(total_tests);
int test_count = 0;
// ============================================================================
// Part 1: Test LOAD/SAVE commands
// ============================================================================
diag("=== Part 1: Testing LOAD/SAVE GENAI VARIABLES commands ===");
for (const auto& query : queries) {
MYSQL* admin_local = mysql_init(NULL);
if (!admin_local) {
diag("Failed to initialize MySQL connection");
continue;
}
if (!mysql_real_connect(admin_local, cl.host, cl.admin_username, cl.admin_password,
NULL, cl.admin_port, NULL, 0)) {
diag("Failed to connect to admin interface");
mysql_close(admin_local);
continue;
}
int rc = run_q(admin_local, query.c_str());
ok(rc == 0, "Command executed successfully: %s", query.c_str());
// For SELECT/SHOW/CHECKSUM style commands, verify result set
if (strncasecmp(query.c_str(), "SELECT ", 7) == 0 ||
strncasecmp(query.c_str(), "SHOW ", 5) == 0 ||
strncasecmp(query.c_str(), "CHECKSUM ", 9) == 0) {
MYSQL_RES* res = mysql_store_result(admin_local);
unsigned long long num_rows = mysql_num_rows(res);
ok(num_rows != 0, "Command returned rows: %s", query.c_str());
mysql_free_result(res);
} else {
// For non-query commands, just mark the test as passed
ok(1, "Command completed: %s", query.c_str());
}
mysql_close(admin_local);
}
// ============================================================================
// Part 2: Test variable access (SET and SELECT)
// ============================================================================
diag("=== Part 2: Testing variable access (SET and SELECT) ===");
test_count += test_variable_access(admin);
// ============================================================================
// Part 3: Test variable persistence across layers
// ============================================================================
diag("=== Part 3: Testing variable persistence across storage layers ===");
test_count += test_variable_persistence(admin);
// ============================================================================
// Part 4: Test CHECKSUM commands
// ============================================================================
diag("=== Part 4: Testing CHECKSUM commands ===");
test_count += test_checksum_commands(admin);
// ============================================================================
// Cleanup
// ============================================================================
mysql_close(admin);
diag("=== All GenAI module tests completed ===");
return exit_status();
}