test(sql): add escape unit tests

5 tests covering: doubled single quotes (King's Land case),
backslash escaping, ordinary text passthrough, control characters
(NUL drop, CR/LF/tab/Ctrl-Z escape sequences), and combined
escapes. Locks in the recent escape expansion that fixed the
multi-line INSERT bug.
This commit is contained in:
Kelsi 2026-05-06 07:47:58 -07:00
parent 5366c53734
commit ea713ae994
2 changed files with 65 additions and 0 deletions

View file

@ -414,6 +414,26 @@ target_link_libraries(test_open_formats PRIVATE catch2_main)
add_test(NAME open_formats COMMAND test_open_formats)
register_test_target(test_open_formats)
# ── test_sql_escape ──────────────────────────────────────────
add_executable(test_sql_escape
test_sql_escape.cpp
${CMAKE_SOURCE_DIR}/tools/editor/sql_exporter.cpp
${CMAKE_SOURCE_DIR}/tools/editor/npc_spawner.cpp
${CMAKE_SOURCE_DIR}/tools/editor/quest_editor.cpp
${CMAKE_SOURCE_DIR}/src/core/logger.cpp
)
target_include_directories(test_sql_escape PRIVATE
${TEST_INCLUDE_DIRS}
${CMAKE_SOURCE_DIR}/tools/editor
)
target_include_directories(test_sql_escape SYSTEM PRIVATE
${TEST_SYSTEM_INCLUDE_DIRS}
${CMAKE_SOURCE_DIR}/extern/nlohmann
)
target_link_libraries(test_sql_escape PRIVATE catch2_main)
add_test(NAME sql_escape COMMAND test_sql_escape)
register_test_target(test_sql_escape)
# ── ASAN / UBSan for test targets ────────────────────────────
if(WOWEE_ENABLE_ASAN AND NOT MSVC)
foreach(_t IN LISTS ALL_TEST_TARGETS)

45
tests/test_sql_escape.cpp Normal file
View file

@ -0,0 +1,45 @@
// Tests for SQLExporter::escape — ensures user-provided strings can't
// produce malformed SQL when emitted into INSERT statements.
#include <catch_amalgamated.hpp>
#include "sql_exporter.hpp"
using namespace wowee::editor;
TEST_CASE("SQLExporter::escape doubles single quotes", "[sql]") {
REQUIRE(SQLExporter::escape("King's Land") == "King''s Land");
REQUIRE(SQLExporter::escape("''''") == "''''''''");
}
TEST_CASE("SQLExporter::escape escapes backslashes", "[sql]") {
REQUIRE(SQLExporter::escape("path\\to\\file") == "path\\\\to\\\\file");
}
TEST_CASE("SQLExporter::escape passes through ordinary text unchanged", "[sql]") {
REQUIRE(SQLExporter::escape("Hello, world!") == "Hello, world!");
REQUIRE(SQLExporter::escape("") == "");
REQUIRE(SQLExporter::escape("Some-Name_123") == "Some-Name_123");
}
TEST_CASE("SQLExporter::escape handles control characters", "[sql]") {
// NUL is dropped (some clients don't respect length-prefixed strings)
std::string withNul("a", 1);
withNul += '\0';
withNul += 'b';
REQUIRE(SQLExporter::escape(withNul) == "ab");
// Newlines/CR/tab become escape sequences so each INSERT stays on one line
REQUIRE(SQLExporter::escape("a\nb") == "a\\nb");
REQUIRE(SQLExporter::escape("a\rb") == "a\\rb");
REQUIRE(SQLExporter::escape("a\tb") == "a\\tb");
// Ctrl-Z (historical MySQL string terminator on Windows)
std::string withCtrlZ;
withCtrlZ += 'a';
withCtrlZ += static_cast<char>(26);
withCtrlZ += 'b';
REQUIRE(SQLExporter::escape(withCtrlZ) == "a\\Zb");
}
TEST_CASE("SQLExporter::escape combines escapes correctly", "[sql]") {
REQUIRE(SQLExporter::escape("O'Brien\\path") == "O''Brien\\\\path");
}