15 #include <cppunit/TestFixture.h>
16 #include <cppunit/extensions/HelperMacros.h>
18 #include "ReuserDatabaseHandler.hpp"
30 bool callIsValidIdentifier(
const std::string&
s)
35 std::string callReplaceUnicodeControlChars(
const std::string&
s)
40 int callGetDecisionTypePriority(
int decisionType)
67 CPPUNIT_TEST_SUITE_END();
77 CPPUNIT_ASSERT(!acc.callIsValidIdentifier(
""));
85 CPPUNIT_ASSERT(acc.callIsValidIdentifier(
"abc"));
93 CPPUNIT_ASSERT(acc.callIsValidIdentifier(
"ABC"));
101 CPPUNIT_ASSERT(acc.callIsValidIdentifier(
"123"));
109 CPPUNIT_ASSERT(acc.callIsValidIdentifier(
"_"));
117 CPPUNIT_ASSERT(acc.callIsValidIdentifier(
"uploadtree_a"));
118 CPPUNIT_ASSERT(acc.callIsValidIdentifier(
"upload_fk_123"));
126 CPPUNIT_ASSERT(!acc.callIsValidIdentifier(
"upload tree"));
134 CPPUNIT_ASSERT(!acc.callIsValidIdentifier(
"upload-tree"));
142 CPPUNIT_ASSERT(!acc.callIsValidIdentifier(
"public.uploadtree"));
150 CPPUNIT_ASSERT(!acc.callIsValidIdentifier(
"foo;DROP TABLE uploadtree"));
158 CPPUNIT_ASSERT(!acc.callIsValidIdentifier(
"up'load"));
166 CPPUNIT_ASSERT(!acc.callIsValidIdentifier(
"up\"load"));
174 CPPUNIT_ASSERT(!acc.callIsValidIdentifier(
"$1"));
182 CPPUNIT_ASSERT(!acc.callIsValidIdentifier(std::string(
"up\x00load", 8)));
193 CPPUNIT_ASSERT(!acc.callIsValidIdentifier(
"t WHERE 1=1--"));
194 CPPUNIT_ASSERT(!acc.callIsValidIdentifier(
"t UNION SELECT 1"));
202 CPPUNIT_ASSERT(acc.callIsValidIdentifier(
"uploadtree"));
203 CPPUNIT_ASSERT(acc.callIsValidIdentifier(
"uploadtree_a"));
205 CPPUNIT_ASSERT(acc.callIsValidIdentifier(
"uploadtree_42"));
227 CPPUNIT_TEST_SUITE_END();
231 std::string call(
const std::string&
s)
233 return acc.callReplaceUnicodeControlChars(
s);
242 CPPUNIT_ASSERT_EQUAL(std::string(
"hello world"), call(
"hello world"));
253 CPPUNIT_ASSERT_EQUAL(std::string(
"\t\n"), call(
"\t\n"));
261 CPPUNIT_ASSERT_EQUAL(std::string(
"\r"), call(
"\r"));
269 std::string in(1,
'\x00');
270 CPPUNIT_ASSERT_EQUAL(std::string(
""), call(in));
282 for (
char c =
'\x01'; c <=
'\x08'; ++c)
284 std::string in(1, c);
285 CPPUNIT_ASSERT_EQUAL_MESSAGE(
286 "Expected control char 0x" + std::to_string((
unsigned char)c) +
" to be stripped",
287 std::string(
""), call(in));
290 CPPUNIT_ASSERT_EQUAL(std::string(
""), call(
"\x0B"));
291 CPPUNIT_ASSERT_EQUAL(std::string(
""), call(
"\x0C"));
293 for (
char c =
'\x0E'; c <=
'\x1F'; ++c)
295 std::string in(1, c);
296 CPPUNIT_ASSERT_EQUAL_MESSAGE(
297 "Expected control char 0x" + std::to_string((
unsigned char)c) +
" to be stripped",
298 std::string(
""), call(in));
311 std::string c1_80 =
"\xC2\x80";
312 CPPUNIT_ASSERT_EQUAL(std::string(
""), call(c1_80));
314 std::string c1_9f =
"\xC2\x9F";
315 CPPUNIT_ASSERT_EQUAL(std::string(
""), call(c1_9f));
323 CPPUNIT_ASSERT_EQUAL(std::string(
""), call(
"\x7F"));
332 std::string copyright_sign =
"\xC2\xA9";
333 CPPUNIT_ASSERT_EQUAL(copyright_sign, call(copyright_sign));
348 std::string emoji =
"\xF0\x9F\x98\x80";
349 CPPUNIT_ASSERT_EQUAL(emoji, call(emoji));
361 std::string in =
"hello\x01world";
362 CPPUNIT_ASSERT_EQUAL(std::string(
"helloworld"), call(in));
366 std::string emoji =
"\xF0\x9F\x98\x80";
367 std::string mixed = emoji +
"\x01" + emoji;
368 CPPUNIT_ASSERT_EQUAL(emoji + emoji, call(mixed));
376 CPPUNIT_ASSERT_EQUAL(std::string(
""), call(
""));
387 CPPUNIT_TEST_SUITE_END();
390 int prio(
int decisionType)
393 return accessor.callGetDecisionTypePriority(decisionType);
399 CPPUNIT_ASSERT(prio(0) < prio(3));
400 CPPUNIT_ASSERT(prio(3) < prio(4));
401 CPPUNIT_ASSERT(prio(4) < prio(7));
402 CPPUNIT_ASSERT(prio(7) < prio(6));
403 CPPUNIT_ASSERT(prio(6) < prio(5));
404 CPPUNIT_ASSERT_EQUAL(0, prio(999));
414 const int irrelevantPk = 50;
415 const int irrelevantType = 4;
416 const int identifiedPk = 40;
417 const int identifiedType = 5;
419 int chosenPk = irrelevantPk;
420 int chosenType = irrelevantType;
422 auto applyRow = [&](
int pk,
int type)
424 if (prio(type) > prio(chosenType))
431 applyRow(irrelevantPk, irrelevantType);
432 applyRow(identifiedPk, identifiedType);
434 CPPUNIT_ASSERT_EQUAL(identifiedPk, chosenPk);
435 CPPUNIT_ASSERT_EQUAL(identifiedType, chosenType);
Mock ReuserDatabaseHandler for unit tests.
void testPriorityOrdering()
Matches PHP ReuserAgent::getDecisionTypePriority.
void testStrongerTypeBeatsWeakerRegardlessOfPkOrder()
Same pfile: IDENTIFIED (pk 40) must win over IRRELEVANT (pk 50).
void testDigitsAreValid()
A string of ASCII digits is valid.
void testDotIsInvalid()
A dot makes an identifier invalid (guards against schema.table injection).
void testLowercaseLettersAreValid()
A string of lowercase ASCII letters is valid.
void testUppercaseLettersAreValid()
A string of uppercase ASCII letters is valid.
void testHyphenIsInvalid()
A hyphen makes an identifier invalid.
void testDoubleQuoteIsInvalid()
A double quote makes an identifier invalid.
void testDollarIsInvalid()
A dollar sign makes an identifier invalid.
void testSemicolonIsInvalid()
A semicolon makes an identifier invalid (guards against statement injection).
void testSqlInjectionPatternIsInvalid()
A classic SQL injection pattern is rejected.
void testMixedAlphanumericUnderscoreIsValid()
A typical mixed alphanumeric/underscore identifier is valid.
void testSingleQuoteIsInvalid()
A single quote makes an identifier invalid.
void testSpaceIsInvalid()
A space character makes an identifier invalid.
void testUnderscoreIsValid()
An underscore character is valid.
void testNullByteIsInvalid()
A null byte makes an identifier invalid.
void testKnownTableNamesAreValid()
The three concrete uploadtree table names used in production are valid.
void testEmptyStringIsInvalid()
An empty string is not a valid SQL identifier.
Test double for ReuserDatabaseHandler.
MockReuserDatabaseHandler()
void testMixedControlAndPrintableFiltered()
A string mixing control characters and printable text is filtered correctly.
void testDeleteCharIsStripped()
DEL character (U+007F) is stripped.
void testEmptyStringIsUnchanged()
An empty input string returns an empty string.
void testC1ControlCharsAreStripped()
C1 control characters U+0080-U+009F are stripped.
void testPlainAsciiIsUnchanged()
Plain ASCII text passes through unchanged.
void testCarriageReturnIsKept()
Carriage return (U+000D) is kept.
void testSurrogatePairCodepointIsKept()
A codepoint above U+FFFF (surrogate-pair range in UTF-16) is kept.
void testC0ControlCharsAreStripped()
C0 control characters U+0001-U+0008 are stripped.
void testTabAndNewlineAreKept()
Horizontal tab (U+0009) and line feed (U+000A) are kept.
void testUtf8MultiBytePrintableIsKept()
A printable multi-byte UTF-8 character (e.g. U+00A9 ©) is kept.
void testNullByteIsStripped()
Null byte (U+0000) is stripped.
static int getDecisionTypePriority(int decisionType)
Priority for decision types during reuse conflict resolution.
static bool isValidIdentifier(const std::string &s)
Validate that s contains only characters safe for SQL identifiers.
static std::string replaceUnicodeControlChars(const std::string &input)
Strip Unicode control characters (C0, C1, DEL) from input.
Thin subclass that exposes private helper methods for testing.
int s
The socket that the CLI will use to communicate.