mirror of https://github.com/sysown/proxysql
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.
222 lines
7.4 KiB
222 lines
7.4 KiB
/**
|
|
* @file sqlite3db_unit-t.cpp
|
|
* @brief Unit tests for SQLite3DB — the foundational database wrapper.
|
|
*
|
|
* Tests cover: execute(), execute_statement(), return_one_int(),
|
|
* check_table_structure(), build_table(), check_and_build_table(),
|
|
* SQLite3_row::get_size(), and locking primitives.
|
|
*
|
|
* All tests use an in-memory SQLite3 database — no external deps.
|
|
*/
|
|
|
|
#include "tap.h"
|
|
#include "test_globals.h"
|
|
#include "test_init.h"
|
|
#include "proxysql.h"
|
|
#include "sqlite3db.h"
|
|
|
|
#include <cstring>
|
|
#include <string>
|
|
|
|
// ============================================================
|
|
// SQLite3DB core operations
|
|
// ============================================================
|
|
|
|
static SQLite3DB *db = nullptr;
|
|
|
|
static void setup_db() {
|
|
db = new SQLite3DB();
|
|
db->open((char *)":memory:", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX);
|
|
}
|
|
|
|
static void teardown_db() {
|
|
delete db;
|
|
db = nullptr;
|
|
}
|
|
|
|
static void test_open_and_execute() {
|
|
bool rc = db->execute("CREATE TABLE test_t (id INTEGER PRIMARY KEY, name TEXT)");
|
|
ok(rc == true, "execute: CREATE TABLE succeeds (returns true)");
|
|
}
|
|
|
|
static void test_execute_insert_select() {
|
|
db->execute("INSERT INTO test_t (id, name) VALUES (1, 'alice')");
|
|
db->execute("INSERT INTO test_t (id, name) VALUES (2, 'bob')");
|
|
|
|
char *error = nullptr;
|
|
SQLite3_result *result = db->execute_statement("SELECT COUNT(*) FROM test_t", &error);
|
|
ok(result != nullptr, "execute_statement: returns result for SELECT");
|
|
ok(result != nullptr && result->rows_count == 1, "execute_statement: COUNT(*) returns 1 row");
|
|
if (result && result->rows_count > 0) {
|
|
ok(strcmp(result->rows[0]->fields[0], "2") == 0,
|
|
"execute_statement: COUNT(*) = 2");
|
|
} else {
|
|
ok(false, "execute_statement: COUNT(*) = 2 (no result)");
|
|
}
|
|
if (error) free(error);
|
|
delete result;
|
|
}
|
|
|
|
// ============================================================
|
|
// return_one_int() — convenience wrapper
|
|
// ============================================================
|
|
|
|
static void test_return_one_int() {
|
|
int val = db->return_one_int("SELECT 42");
|
|
ok(val == 42, "return_one_int: SELECT 42 returns 42");
|
|
}
|
|
|
|
static void test_return_one_int_from_table() {
|
|
int val = db->return_one_int("SELECT COUNT(*) FROM test_t");
|
|
ok(val == 2, "return_one_int: COUNT(*) from test_t = 2");
|
|
}
|
|
|
|
static void test_return_one_int_no_rows() {
|
|
int val = db->return_one_int("SELECT id FROM test_t WHERE id = 999");
|
|
ok(val == 0, "return_one_int: no rows returns 0");
|
|
}
|
|
|
|
// ============================================================
|
|
// check_table_structure() — schema validation
|
|
// ============================================================
|
|
|
|
static void test_check_table_structure_match() {
|
|
// The CREATE statement we used
|
|
int match = db->check_table_structure(
|
|
(char *)"test_t",
|
|
(char *)"CREATE TABLE test_t (id INTEGER PRIMARY KEY, name TEXT)"
|
|
);
|
|
ok(match == 1, "check_table_structure: matching schema returns 1");
|
|
}
|
|
|
|
static void test_check_table_structure_mismatch() {
|
|
int match = db->check_table_structure(
|
|
(char *)"test_t",
|
|
(char *)"CREATE TABLE test_t (id INTEGER, extra_col TEXT)"
|
|
);
|
|
ok(match == 0, "check_table_structure: mismatched schema returns 0");
|
|
}
|
|
|
|
static void test_check_table_structure_nonexistent() {
|
|
int match = db->check_table_structure(
|
|
(char *)"nonexistent_table",
|
|
(char *)"CREATE TABLE nonexistent_table (id INT)"
|
|
);
|
|
ok(match == 0, "check_table_structure: nonexistent table returns 0");
|
|
}
|
|
|
|
// ============================================================
|
|
// build_table() / check_and_build_table()
|
|
// ============================================================
|
|
|
|
static void test_build_table() {
|
|
bool rc = db->build_table(
|
|
(char *)"new_table",
|
|
(char *)"CREATE TABLE new_table (val TEXT)",
|
|
false // don't drop if exists
|
|
);
|
|
ok(rc == true, "build_table: creates new table successfully");
|
|
|
|
// Verify it exists
|
|
int count = db->return_one_int("SELECT COUNT(*) FROM new_table");
|
|
ok(count == 0, "build_table: new table is empty");
|
|
}
|
|
|
|
static void test_build_table_drop_recreate() {
|
|
// Insert data first
|
|
db->execute("INSERT INTO new_table VALUES ('data')");
|
|
int before = db->return_one_int("SELECT COUNT(*) FROM new_table");
|
|
ok(before == 1, "build_table: table has 1 row before drop");
|
|
|
|
bool rc = db->build_table(
|
|
(char *)"new_table",
|
|
(char *)"CREATE TABLE new_table (val TEXT)",
|
|
true // drop and recreate
|
|
);
|
|
ok(rc == true, "build_table: drop+recreate succeeds");
|
|
|
|
int after = db->return_one_int("SELECT COUNT(*) FROM new_table");
|
|
ok(after == 0, "build_table: table is empty after drop+recreate");
|
|
}
|
|
|
|
static void test_check_and_build_table_creates() {
|
|
db->check_and_build_table(
|
|
(char *)"auto_table",
|
|
(char *)"CREATE TABLE auto_table (x INT)"
|
|
);
|
|
int count = db->return_one_int("SELECT COUNT(*) FROM auto_table");
|
|
ok(count == 0, "check_and_build_table: creates table if not exists");
|
|
}
|
|
|
|
static void test_check_and_build_table_idempotent() {
|
|
db->execute("INSERT INTO auto_table VALUES (1)");
|
|
db->check_and_build_table(
|
|
(char *)"auto_table",
|
|
(char *)"CREATE TABLE auto_table (x INT)"
|
|
);
|
|
int count = db->return_one_int("SELECT COUNT(*) FROM auto_table");
|
|
ok(count == 1, "check_and_build_table: idempotent -- data preserved");
|
|
}
|
|
|
|
// ============================================================
|
|
// SQLite3_row — row operations
|
|
// ============================================================
|
|
|
|
static void test_sqlite3_row_get_size() {
|
|
char *error = nullptr;
|
|
SQLite3_result *result = db->execute_statement("SELECT id, name FROM test_t WHERE id = 1", &error);
|
|
ok(result != nullptr && result->rows_count == 1, "row_get_size: got 1 row");
|
|
if (result && result->rows_count > 0) {
|
|
unsigned long long sz = result->rows[0]->get_size();
|
|
ok(sz > 0, "row_get_size: size is non-zero");
|
|
} else {
|
|
ok(false, "row_get_size: size is non-zero (no result)");
|
|
}
|
|
if (error) free(error);
|
|
delete result;
|
|
}
|
|
|
|
// ============================================================
|
|
// Locking operations (basic sanity)
|
|
// ============================================================
|
|
|
|
static void test_rdlock_unlock() {
|
|
// Just verify no crash/deadlock on lock+unlock
|
|
db->rdlock();
|
|
db->rdunlock();
|
|
ok(true, "rdlock/rdunlock: no crash");
|
|
}
|
|
|
|
static void test_wrlock_unlock() {
|
|
db->wrlock();
|
|
db->wrunlock();
|
|
ok(true, "wrlock/wrunlock: no crash");
|
|
}
|
|
|
|
int main() {
|
|
plan(21);
|
|
test_init_minimal();
|
|
|
|
setup_db();
|
|
|
|
test_open_and_execute(); // 1
|
|
test_execute_insert_select(); // 3
|
|
test_return_one_int(); // 1
|
|
test_return_one_int_from_table(); // 1
|
|
test_return_one_int_no_rows(); // 1
|
|
test_check_table_structure_match(); // 1
|
|
test_check_table_structure_mismatch(); // 1
|
|
test_check_table_structure_nonexistent(); // 1
|
|
test_build_table(); // 2
|
|
test_build_table_drop_recreate(); // 3
|
|
test_check_and_build_table_creates(); // 1
|
|
test_check_and_build_table_idempotent();// 1
|
|
test_sqlite3_row_get_size(); // 2
|
|
test_rdlock_unlock(); // 1
|
|
test_wrlock_unlock(); // 1
|
|
|
|
teardown_db();
|
|
test_cleanup_minimal();
|
|
return exit_status();
|
|
}
|