Adding unit tests

This commit is contained in:
Miroslav Štampar 2026-06-15 09:50:47 +02:00
parent 48b915b5ee
commit 3816df1241
39 changed files with 3501 additions and 2 deletions

87
tests/test_sqlparse.py Normal file
View file

@ -0,0 +1,87 @@
#!/usr/bin/env python
"""
Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org)
See the file 'LICENSE' for copying permission
SQL/string parsing helpers: field splitting and 0-depth (paren+quote aware)
scanning, query cleanup, regex extraction.
Includes regression cases for the quote-awareness bugs fixed previously.
"""
import os
import sys
import unittest
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from _testutils import bootstrap
bootstrap()
from lib.core.common import splitFields, zeroDepthSearch, cleanQuery, extractRegexResult
class TestSplitFields(unittest.TestCase):
CASES = [
("a,b", ["a", "b"]),
("user,password", ["user", "password"]),
("a,b,c", ["a", "b", "c"]),
("a", ["a"]),
("max(a,b)", ["max(a,b)"]), # paren-protected
("max(a, b),c", ["max(a,b)", "c"]), # ', ' normalized; outer split
("COUNT(*),name", ["COUNT(*)", "name"]),
("f(g(x,y),z),h", ["f(g(x,y),z)", "h"]), # nested parens
("'a,b'", ["'a,b'"]), # REGRESSION: comma in single-quoted literal
("'a,b','c|d','e&f'", ["'a,b'", "'c|d'", "'e&f'"]), # REGRESSION
('"x,y",z', ['"x,y"', "z"]), # double-quoted literal
]
def test_table(self):
for inp, expected in self.CASES:
self.assertEqual(splitFields(inp), expected, msg="splitFields(%r)" % inp)
class TestZeroDepthSearch(unittest.TestCase):
def test_quote_awareness(self):
# ' FROM ' inside a literal must NOT be a clause boundary (regression)
self.assertEqual(zeroDepthSearch("SELECT 'x FROM y'", " FROM "), [])
# a real FROM must be found (exactly once here)
self.assertEqual(len(zeroDepthSearch("SELECT a FROM t", " FROM ")), 1)
def test_paren_awareness(self):
self.assertEqual(zeroDepthSearch("a(,)b,c", ","), [5]) # only the depth-0 comma
def test_doctest_vectors(self):
q = "SELECT (SELECT id FROM users WHERE 2>1) AS result FROM DUAL"
hits = zeroDepthSearch(q, "FROM")
self.assertTrue(hits, "no depth-0 FROM found") # guard: avoid a confusing IndexError
self.assertEqual(q[hits[0]:], "FROM DUAL") # outer FROM only
s = "a(b; c),d;e"
hits = zeroDepthSearch(s, "[;, ]")
self.assertTrue(hits)
self.assertEqual(s[hits[0]:], ",d;e") # char-class form
class TestCleanQuery(unittest.TestCase):
def test_keyword_uppercasing(self):
self.assertEqual(cleanQuery("select a from t"), "SELECT a FROM t")
# mixed case keywords get uppercased; non-keyword identifiers are preserved verbatim
self.assertEqual(cleanQuery("seLeCt a fRoM t"), "SELECT a FROM t")
self.assertEqual(cleanQuery("SELECT 1"), "SELECT 1") # already-upper unchanged
def test_idempotent(self):
for q in ["select a from t", "SELECT 1", "select x where y=1 order by z"]:
once = cleanQuery(q)
self.assertEqual(cleanQuery(once), once)
# idempotence alone would pass even if cleanQuery uppercased EVERYTHING; anchor that it
# uppercases keywords but preserves the lowercase identifier
self.assertEqual(cleanQuery("select a from t"), "SELECT a FROM t")
class TestExtractRegexResult(unittest.TestCase):
def test_named_group(self):
self.assertEqual(extractRegexResult(r"id=(?P<result>\d+)", "id=42"), "42")
self.assertIsNone(extractRegexResult(r"id=(?P<result>\d+)", "no match here"))
if __name__ == "__main__":
unittest.main(verbosity=2)