import os import sys import unittest import base64 import snappy # Add project root to path sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../..'))) try: from o11ydiagnosis.subtasks.slowlogconverter.parse_query import parse_binary_plan PARSE_QUERY_AVAILABLE = True except ImportError: PARSE_QUERY_AVAILABLE = False parse_binary_plan = None class TestParseQuery(unittest.TestCase): @unittest.skipUnless(PARSE_QUERY_AVAILABLE, "parse_query module not available") def test_parse_binary_plan_valid(self): """Test parsing a valid binary plan string""" # This is a sample binary plan string from the code binary_plan = "tidb_decode_binary_plan('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')" result = parse_binary_plan(binary_plan) # Should return a JSON string (decoded plan) self.assertIsNotNone(result) self.assertIsInstance(result, str) # Should contain some expected fields from the plan # The exact content depends on the plan structure @unittest.skipUnless(PARSE_QUERY_AVAILABLE, "parse_query module not available") def test_parse_binary_plan_invalid_format(self): """Test parsing an invalid binary plan format""" # Missing prefix result = parse_binary_plan("invalid_format") self.assertIsNone(result) # Missing suffix result = parse_binary_plan("tidb_decode_binary_plan('invalid") self.assertIsNone(result) # Empty string result = parse_binary_plan("") self.assertIsNone(result) # None result = parse_binary_plan(None) self.assertIsNone(result) @unittest.skipUnless(PARSE_QUERY_AVAILABLE, "parse_query module not available") def test_parse_binary_plan_invalid_base64(self): """Test parsing with invalid base64 data""" # Invalid base64 string binary_plan = "tidb_decode_binary_plan('invalid_base64!!!')" result = parse_binary_plan(binary_plan) # Should return None due to decode error self.assertIsNone(result) @unittest.skipUnless(PARSE_QUERY_AVAILABLE, "parse_query module not available") def test_parse_binary_plan_caching(self): """Test that parse_binary_plan uses LRU cache""" binary_plan = "tidb_decode_binary_plan('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')" # First call result1 = parse_binary_plan(binary_plan) # Second call with same input (should use cache) result2 = parse_binary_plan(binary_plan) # Results should be the same self.assertEqual(result1, result2) # The function should be cached (we can't directly test cache hits, # but we can verify it doesn't fail on repeated calls) if __name__ == '__main__': unittest.main()