Add unit test Makefile and smoke test

Adds the build system and initial smoke test for the unit test
framework (Phase 2.1, #5473).

- test/tap/tests/unit/Makefile: Compiles unit test binaries linked
  against libproxysql.a + test_globals.o. Compiles tap.o directly
  from tap.cpp to avoid the cpp-dotenv dependency chain, making unit
  tests buildable on both Linux and macOS. Handles platform-specific
  linker flags (Darwin vs Linux).

- test/tap/tests/unit/smoke_test-t.cpp: Validates the test harness
  by exercising test_init_minimal() (GloVars setup), test_init_auth()
  (MySQL_Authentication add/lookup/exists cycle), and idempotency of
  all init/cleanup functions. Runs in <1 second with no Docker or
  network dependencies.
pull/5486/head
René Cannaò 2 months ago
parent 803eb27590
commit 43c04fb394

@ -0,0 +1,263 @@
#!/bin/make -f
#
# Makefile for ProxySQL unit tests.
#
# Unit tests link against libproxysql.a with stub globals (test_globals.o)
# instead of main.o, allowing individual components to be tested in
# isolation without a running ProxySQL daemon or backend servers.
#
# See: GitHub issue #5473 (Phase 2.1: Test Infrastructure Foundation)
PROXYSQL_PATH := $(shell while [ ! -f ./src/proxysql_global.cpp ]; do cd ..; done; pwd)
include $(PROXYSQL_PATH)/include/makefiles_vars.mk
include $(PROXYSQL_PATH)/include/makefiles_paths.mk
# ===========================================================================
# Include directories — mirrors test/tap/tests/Makefile
# ===========================================================================
IDIRS := -I$(TAP_IDIR) \
-I$(RE2_IDIR) \
-I$(PROXYSQL_IDIR) \
-I$(JEMALLOC_IDIR) \
-I$(LIBCONFIG_IDIR) \
-I$(MARIADB_IDIR) \
-I$(LIBDAEMON_IDIR) \
-I$(MICROHTTPD_IDIR) \
-I$(LIBHTTPSERVER_IDIR) \
-I$(CURL_IDIR) -I$(EV_IDIR) \
-I$(PROMETHEUS_IDIR) \
-I$(DOTENV_DYN_IDIR) \
-I$(SQLITE3_IDIR) \
-I$(JSON_IDIR) \
-I$(POSTGRESQL_IDIR) \
-I$(LIBSCRAM_IDIR) \
-I$(LIBUSUAL_IDIR) \
-I$(SSL_IDIR) \
-I$(ZSTD_IDIR) \
-I$(PROXYSQL_PATH)/include \
-I$(PROXYSQL_PATH)/test/tap/test_helpers
# ===========================================================================
# Library directories
# ===========================================================================
LDIRS := -L$(TAP_LDIR) \
-L$(RE2_LDIR) \
-L$(PROXYSQL_LDIR) \
-L$(JEMALLOC_LDIR) \
-L$(LIBCONFIG_LDIR) \
-L$(MARIADB_LDIR) \
-L$(LIBDAEMON_LDIR) \
-L$(MICROHTTPD_LDIR) \
-L$(LIBHTTPSERVER_LDIR) \
-L$(CURL_LDIR) -L$(EV_LDIR) \
-L$(PROMETHEUS_LDIR) \
-L$(DOTENV_DYN_LDIR) \
-L$(PCRE_LDIR) \
-L$(LIBINJECTION_LDIR) \
-L$(POSTGRESQL_LDIR) \
-L$(LIBSCRAM_LDIR) \
-L$(LIBUSUAL_LDIR) \
-L$(SSL_LDIR)
ifeq ($(UNAME_S),Linux)
LDIRS += -L$(COREDUMPER_LDIR)
endif
ifeq ($(UNAME_S),Darwin)
IDIRS += -I/usr/local/include -I/opt/homebrew/include
LDIRS += -L/usr/local/lib -L/opt/homebrew/lib
endif
# ===========================================================================
# ClickHouse include/link paths (enabled by default)
# ===========================================================================
CLICKHOUSE_CPP_PATH := $(DEPS_PATH)/clickhouse-cpp/clickhouse-cpp
CLICKHOUSE_CPP_IDIR := $(CLICKHOUSE_CPP_PATH) -I$(CLICKHOUSE_CPP_PATH)/contrib/absl
CLICKHOUSE_CPP_LDIR := $(CLICKHOUSE_CPP_PATH)/clickhouse
LZ4_LDIR := $(DEPS_PATH)/lz4/lz4/lib
IDIRS += -I$(CLICKHOUSE_CPP_IDIR)
# ===========================================================================
# libproxysql.a — the core library under test
# ===========================================================================
LIBPROXYSQLAR := $(PROXYSQL_LDIR)/libproxysql.a
# ===========================================================================
# Static libraries required at link time
# ===========================================================================
STATIC_LIBS := $(CITYHASH_LDIR)/libcityhash.a \
$(LZ4_LDIR)/liblz4.a \
$(ZSTD_LDIR)/libzstd.a
ifeq ($(PROXYSQLCLICKHOUSE),1)
STATIC_LIBS += $(CLICKHOUSE_CPP_LDIR)/libclickhouse-cpp-lib.a
endif
ifeq ($(UNAME_S),Linux)
STATIC_LIBS += $(COREDUMPER_LDIR)/libcoredumper.a
endif
ifeq ($(PROXYSQLGENAI),1)
STATIC_LIBS += $(SQLITE3_LDIR)/../libsqlite_rembed.a $(SQLITE3_LDIR)/vec.o
endif
# ===========================================================================
# Linker flags — platform-specific
# ===========================================================================
ifeq ($(UNAME_S),Darwin)
# macOS: No -Bstatic/-Bdynamic; use explicit .a paths for static linking.
# libproxysql.a already bundles most deps on Darwin (see src/Makefile).
LIBPROXYSQLAR_FULL := $(LIBPROXYSQLAR) \
$(JEMALLOC_LDIR)/libjemalloc.a \
$(MICROHTTPD_LDIR)/libmicrohttpd.a \
$(LIBHTTPSERVER_LDIR)/libhttpserver.a \
$(PCRE_LDIR)/libpcre.a \
$(PCRE_LDIR)/libpcrecpp.a \
$(LIBDAEMON_LDIR)/libdaemon.a \
$(LIBCONFIG_LDIR)/libconfig++.a \
$(LIBCONFIG_LDIR)/libconfig.a \
$(CURL_LDIR)/libcurl.a \
$(SQLITE3_LDIR)/sqlite3.o \
$(LIBINJECTION_LDIR)/libinjection.a \
$(EV_LDIR)/libev.a \
$(LIBSCRAM_LDIR)/libscram.a \
$(LIBUSUAL_LDIR)/libusual.a \
$(MARIADB_LDIR)/libmariadbclient.a \
$(RE2_LDIR)/libre2.a \
$(POSTGRESQL_PATH)/interfaces/libpq/libpq.a \
$(POSTGRESQL_PATH)/common/libpgcommon.a \
$(POSTGRESQL_PATH)/port/libpgport.a
MYLIBS := -lssl -lcrypto -lpthread -lm -lz \
-liconv -lgnutls -lprometheus-cpp-pull -lprometheus-cpp-core -luuid \
-lzstd $(LWGCOV)
else
# Linux/FreeBSD: Use -Bstatic/-Bdynamic for controlled linking.
LIBPROXYSQLAR_FULL := $(LIBPROXYSQLAR)
MYLIBS := -Wl,--export-dynamic -Wl,-Bdynamic -lgnutls -lcurl -lssl -lcrypto -luuid \
-Wl,-Bstatic -lconfig -lproxysql -ldaemon -lconfig++ -lre2 -lpcrecpp -lpcre \
-lmariadbclient -lhttpserver -lmicrohttpd -linjection -lev \
-lprometheus-cpp-pull -lprometheus-cpp-core \
-Wl,-Bstatic -lpq -lpgcommon -lpgport \
-Wl,-Bdynamic -lpthread -lm -lz -lzstd -lrt -ldl \
-lscram -lusual -Wl,--allow-multiple-definition \
$(LWGCOV)
endif
ifneq ($(NOJEMALLOC),1)
ifeq ($(UNAME_S),Linux)
MYLIBS += -Wl,-Bstatic -ljemalloc -Wl,-Bdynamic
endif
endif
# ===========================================================================
# Compiler flags
# ===========================================================================
PSQLCH :=
ifeq ($(PROXYSQLCLICKHOUSE),1)
PSQLCH := -DPROXYSQLCLICKHOUSE
endif
PSQLGA :=
ifeq ($(PROXYSQLGENAI),1)
PSQLGA := -DPROXYSQLGENAI
endif
PSQL31 :=
ifeq ($(PROXYSQL31),1)
PSQL31 := -DPROXYSQL31
endif
PSQLFFTO :=
ifeq ($(PROXYSQLFFTO),1)
PSQLFFTO := -DPROXYSQLFFTO
endif
PSQLTSDB :=
ifeq ($(PROXYSQLTSDB),1)
PSQLTSDB := -DPROXYSQLTSDB
endif
OPT := $(STDCPP) -O0 -ggdb $(PSQLCH) $(PSQLGA) $(PSQL31) $(PSQLFFTO) $(PSQLTSDB) \
-DGITVERSION=\"$(GIT_VERSION)\" $(NOJEM) $(WGCOV) $(WASAN) \
-Wl,--no-as-needed -Wl,-rpath,$(TAP_LDIR)
ifeq ($(UNAME_S),Darwin)
OPT := $(STDCPP) -O0 -ggdb $(PSQLCH) $(PSQLGA) $(PSQL31) $(PSQLFFTO) $(PSQLTSDB) \
-DGITVERSION=\"$(GIT_VERSION)\" $(NOJEM) $(WGCOV) $(WASAN)
endif
# ===========================================================================
# Test helper objects
# ===========================================================================
TEST_HELPERS_DIR := $(PROXYSQL_PATH)/test/tap/test_helpers
ODIR := obj
TEST_HELPERS_OBJ := $(ODIR)/test_globals.o $(ODIR)/test_init.o $(ODIR)/tap.o
$(ODIR):
mkdir -p $(ODIR)
# Compile tap.o directly from tap.cpp to avoid the full TAP build chain
# and its cpp-dotenv dependency (which doesn't build on macOS).
# Unit tests only need the core TAP functions: plan(), ok(), is(), etc.
TAP_SRC := $(TAP_PATH)/tap.cpp
$(ODIR)/tap.o: $(TAP_SRC) | $(ODIR)
$(CXX) -c -o $@ $< $(OPT) $(IDIRS) -w
$(ODIR)/test_globals.o: $(TEST_HELPERS_DIR)/test_globals.cpp | $(ODIR)
$(CXX) -c -o $@ $< $(OPT) $(IDIRS) -Wall
$(ODIR)/test_init.o: $(TEST_HELPERS_DIR)/test_init.cpp | $(ODIR)
$(CXX) -c -o $@ $< $(OPT) $(IDIRS) -Wall
# ===========================================================================
# Unit test targets
# ===========================================================================
UNIT_TESTS := smoke_test-t
.DEFAULT: default
.PHONY: default debug all
default: all
debug: OPT += -DDEBUG
debug: all
all: $(UNIT_TESTS)
ALLOW_MULTI_DEF :=
ifneq ($(UNAME_S),Darwin)
ALLOW_MULTI_DEF := -Wl,--allow-multiple-definition
endif
smoke_test-t: smoke_test-t.cpp $(TEST_HELPERS_OBJ) $(LIBPROXYSQLAR)
$(CXX) $< $(TEST_HELPERS_OBJ) $(IDIRS) $(LDIRS) $(OPT) \
$(LIBPROXYSQLAR_FULL) $(STATIC_LIBS) $(MYLIBS) \
$(ALLOW_MULTI_DEF) -o $@
# ===========================================================================
# Clean
# ===========================================================================
.PHONY: clean
.SILENT: clean
clean:
rm -rf $(ODIR) $(UNIT_TESTS)

@ -0,0 +1,111 @@
/**
* @file smoke_test-t.cpp
* @brief Smoke test for the ProxySQL unit test harness.
*
* Validates that the test infrastructure (test_globals + test_init)
* works correctly by performing minimal operations on each supported
* component. This test must pass before any component-specific unit
* tests can be trusted.
*
* Test coverage:
* 1. test_init_minimal() GloVars is usable
* 2. test_init_auth() MySQL_Authentication add/lookup cycle
* 3. test_cleanup_*() clean shutdown without leaks
*
* @see Phase 2.1 of the Unit Testing Framework (GitHub issue #5473)
*/
#include "tap.h"
#include "test_globals.h"
#include "test_init.h"
#include "proxysql.h"
#include "MySQL_Authentication.hpp"
#include "PgSQL_Authentication.h"
// Extern declarations for Glo* pointers (defined in test_globals.cpp)
extern MySQL_Authentication *GloMyAuth;
extern PgSQL_Authentication *GloPgAuth;
/**
* @brief Test that minimal initialization sets up GloVars correctly.
*/
static void test_minimal_init() {
int rc = test_init_minimal();
ok(rc == 0, "test_init_minimal() returns 0");
ok(GloVars.datadir != nullptr, "GloVars.datadir is set after init");
ok(GloVars.global.nostart == true, "GloVars.global.nostart is true");
}
/**
* @brief Test MySQL_Authentication add/lookup/del cycle.
*/
static void test_mysql_auth_basic() {
int rc = test_init_auth();
ok(rc == 0, "test_init_auth() returns 0");
ok(GloMyAuth != nullptr, "GloMyAuth is initialized");
ok(GloPgAuth != nullptr, "GloPgAuth is initialized");
// Add a frontend user
bool added = GloMyAuth->add(
(char *)"testuser", // username
(char *)"testpass", // password
USERNAME_FRONTEND, // user type
false, // use_ssl
0, // default_hostgroup
(char *)"", // default_schema
false, // schema_locked
false, // transaction_persistent
false, // fast_forward
100, // max_connections
(char *)"", // attributes
(char *)"" // comment
);
ok(added == true, "GloMyAuth->add() succeeds for frontend user");
// Verify user exists
bool exists = GloMyAuth->exists((char *)"testuser");
ok(exists == true, "GloMyAuth->exists() returns true for added user");
// Verify user does not exist
bool not_exists = GloMyAuth->exists((char *)"nonexistent");
ok(not_exists == false, "GloMyAuth->exists() returns false for unknown user");
// Cleanup
test_cleanup_auth();
ok(GloMyAuth == nullptr, "GloMyAuth is nullptr after cleanup");
ok(GloPgAuth == nullptr, "GloPgAuth is nullptr after cleanup");
}
/**
* @brief Test idempotency of init/cleanup functions.
*/
static void test_idempotency() {
// Double init should be safe
int rc1 = test_init_minimal();
int rc2 = test_init_minimal();
ok(rc1 == 0 && rc2 == 0, "test_init_minimal() is idempotent");
int rc3 = test_init_auth();
int rc4 = test_init_auth();
ok(rc3 == 0 && rc4 == 0, "test_init_auth() is idempotent");
// Double cleanup should be safe
test_cleanup_auth();
test_cleanup_auth(); // should not crash
ok(1, "test_cleanup_auth() double-call does not crash");
test_cleanup_minimal();
test_cleanup_minimal(); // should not crash
ok(1, "test_cleanup_minimal() double-call does not crash");
}
int main() {
plan(15);
test_minimal_init();
test_mysql_auth_basic();
test_idempotency();
return exit_status();
}
Loading…
Cancel
Save