import subprocess
import pytest
from pathlib import Path
from io import StringIO
import pandas as pd
from datetime import datetime, timedelta
import math
class TestMathDateFunctions:
@classmethod
def setup_class(cls):
cls.project_root = Path(__file__).parent.parent
cls.sql_cli = str(cls.project_root / "target" / "release" / "sql-cli")
if not Path(cls.sql_cli).exists():
subprocess.run(["cargo", "build", "--release"],
cwd=cls.project_root, check=True)
cls.generate_test_data()
@classmethod
def generate_test_data(cls):
date_csv = cls.project_root / "data" / "test_dates.csv"
if not date_csv.exists():
with open(date_csv, 'w') as f:
f.write("id,date1,date2,timestamp\n")
f.write("1,2024-01-01,2024-01-15,2024-01-01 10:30:00\n")
f.write("2,2024-02-01,2024-02-10,2024-02-01 14:45:00\n")
f.write("3,2024-03-15,2024-04-15,2024-03-15 09:00:00\n")
f.write("4,2024-06-01,2024-06-30,2024-06-01 12:00:00\n")
f.write("5,2024-12-25,2025-01-05,2024-12-25 00:00:00\n")
def run_query(self, csv_file: str, query: str):
cmd = [
self.sql_cli,
str(self.project_root / "data" / csv_file),
"-q", query,
"-o", "csv"
]
result = subprocess.run(cmd, capture_output=True, text=True, timeout=5)
if result.returncode != 0:
return None, result.stderr
if result.stdout.strip():
return pd.read_csv(StringIO(result.stdout.strip())), None
return pd.DataFrame(), None
def test_floor_function(self):
df, _ = self.run_query("test_simple_math.csv",
"SELECT id, FLOOR(c) as result FROM test_simple_math WHERE id IN (1, 2, 3)")
assert len(df) == 3
assert df[df['id'] == 1]['result'].iloc[0] == 0 assert df[df['id'] == 2]['result'].iloc[0] == 1 assert df[df['id'] == 3]['result'].iloc[0] == 1
def test_ceil_function(self):
df, _ = self.run_query("test_simple_math.csv",
"SELECT id, CEIL(c) as result FROM test_simple_math WHERE id IN (1, 2, 3)")
assert len(df) == 3
assert df[df['id'] == 1]['result'].iloc[0] == 1 assert df[df['id'] == 2]['result'].iloc[0] == 1 assert df[df['id'] == 3]['result'].iloc[0] == 2
def test_ceiling_alias(self):
df, _ = self.run_query("test_simple_math.csv",
"SELECT id, CEILING(c) as result FROM test_simple_math WHERE id = 1")
assert len(df) == 1
assert df.iloc[0]['result'] == 1
def test_pi_function(self):
df, _ = self.run_query("test_simple_math.csv",
"SELECT id, PI() as pi_value FROM test_simple_math WHERE id = 1")
assert len(df) == 1
assert abs(df.iloc[0]['pi_value'] - math.pi) < 0.0001
def test_exp_function(self):
df, _ = self.run_query("test_simple_math.csv",
"SELECT id, ROUND(EXP(a), 2) as result FROM test_simple_math WHERE id IN (1, 2)")
assert len(df) == 2
assert abs(df[df['id'] == 1]['result'].iloc[0] - 2.72) < 0.01 assert abs(df[df['id'] == 2]['result'].iloc[0] - 7.39) < 0.01
def test_ln_function(self):
df, _ = self.run_query("test_simple_math.csv",
"SELECT id, ROUND(LN(a), 2) as result FROM test_simple_math WHERE id IN (1, 3, 5)")
if df is None:
pytest.skip("LN function may not be implemented yet")
assert len(df) == 3
assert df[df['id'] == 1]['result'].iloc[0] == 0
def test_log_function(self):
df, _ = self.run_query("test_simple_math.csv",
"SELECT id, ROUND(LOG(b), 2) as result FROM test_simple_math WHERE id = 10")
if df is None:
pytest.skip("LOG function may not be implemented yet")
assert len(df) == 1
assert abs(df.iloc[0]['result'] - 2.0) < 0.01
def test_log10_function(self):
df, _ = self.run_query("test_simple_math.csv",
"SELECT id, LOG10(b) as result FROM test_simple_math WHERE id = 10")
if df is None:
pytest.skip("LOG10 function may not be implemented yet")
assert len(df) == 1
assert df.iloc[0]['result'] == 2
def test_quotient_function(self):
df, _ = self.run_query("test_simple_math.csv",
"SELECT id, QUOTIENT(b, a) as result FROM test_simple_math WHERE id IN (3, 5, 7)")
if df is None:
pytest.skip("QUOTIENT function may not be implemented yet")
assert len(df) == 3
assert df[df['id'] == 3]['result'].iloc[0] == 10 assert df[df['id'] == 5]['result'].iloc[0] == 10 assert df[df['id'] == 7]['result'].iloc[0] == 10
def test_datediff_function(self):
df, _ = self.run_query("test_dates.csv",
"SELECT id, DATEDIFF('day', order_date, ship_date) as days_diff FROM test_dates WHERE id IN (1, 2, 3)")
if df is None:
pytest.skip("DATEDIFF function may not be implemented yet")
assert len(df) == 3
assert df[df['id'] == 1]['days_diff'].iloc[0] == 1
assert df[df['id'] == 2]['days_diff'].iloc[0] == 1
assert df[df['id'] == 3]['days_diff'].iloc[0] == 1
def test_dateadd_days(self):
df, _ = self.run_query("test_dates.csv",
"SELECT id, DATEADD('day', 10, order_date) as new_date FROM test_dates WHERE id = 1")
if df is None:
pytest.skip("DATEADD function may not be implemented yet")
assert len(df) == 1
assert '2024-01-25' in str(df.iloc[0]['new_date'])
def test_dateadd_months(self):
df, _ = self.run_query("test_dates.csv",
"SELECT id, DATEADD('month', 1, order_date) as new_date FROM test_dates WHERE id = 1")
if df is None:
pytest.skip("DATEADD function may not be implemented yet")
assert len(df) == 1
assert '2024-02-15' in str(df.iloc[0]['new_date'])
def test_dateadd_years(self):
df, _ = self.run_query("test_dates.csv",
"SELECT id, DATEADD('year', 1, order_date) as new_date FROM test_dates WHERE id = 5")
if df is None:
pytest.skip("DATEADD function may not be implemented yet")
assert len(df) == 1
assert '2025' in str(df.iloc[0]['new_date'])
def test_now_function(self):
df, _ = self.run_query("test_simple_math.csv",
"SELECT id, NOW() as current_time FROM test_simple_math WHERE id = 1")
if df is None:
pytest.skip("NOW function may not be implemented yet")
assert len(df) == 1
result = str(df.iloc[0]['current_time'])
assert len(result) > 10
def test_today_function(self):
df, _ = self.run_query("test_simple_math.csv",
"SELECT id, TODAY() as current_date FROM test_simple_math WHERE id = 1")
if df is None:
pytest.skip("TODAY function may not be implemented yet")
assert len(df) == 1
result = str(df.iloc[0]['current_date'])
assert len(result) >= 10
def test_textjoin_function(self):
df, _ = self.run_query("test_simple_strings.csv",
"SELECT TEXTJOIN(',', 1, name, email) as joined FROM test_simple_strings WHERE id = 1")
if df is None:
pytest.skip("TEXTJOIN function may not be implemented yet")
assert len(df) == 1
assert df.iloc[0]['joined'] == 'Alice,alice@example.com'
def test_textjoin_with_multiple_values(self):
df, _ = self.run_query("test_simple_strings.csv",
"SELECT TEXTJOIN(' - ', 1, name, status, code) as joined FROM test_simple_strings WHERE id = 1")
if df is None:
pytest.skip("TEXTJOIN function may not be implemented yet")
assert len(df) == 1
assert df.iloc[0]['joined'] == 'Alice - Active - ABC123'
def test_complex_math_expression(self):
df, _ = self.run_query("test_simple_math.csv",
"SELECT id, ROUND(SQRT(POWER(a, 2) + POWER(b/10, 2)), 2) as result FROM test_simple_math WHERE id = 3")
assert len(df) == 1
assert abs(df.iloc[0]['result'] - 4.24) < 0.01
def test_nested_math_functions(self):
df, _ = self.run_query("test_simple_math.csv",
"SELECT id, CEIL(LOG10(ABS(d - a))) as result FROM test_simple_math WHERE id = 10")
if df is None:
pytest.skip("LOG10 function may not be implemented yet")
assert len(df) == 1
assert df.iloc[0]['result'] == 2
def test_math_with_pi(self):
df, _ = self.run_query("test_simple_math.csv",
"SELECT id, ROUND(a * PI(), 2) as result FROM test_simple_math WHERE id = 2")
if df is None:
pytest.skip("PI function may not be implemented yet")
assert len(df) == 1
assert abs(df.iloc[0]['result'] - 6.28) < 0.01
def test_division_by_zero(self):
df, err = self.run_query("test_simple_math.csv",
"SELECT id, b / (a - a) as result FROM test_simple_math WHERE id = 1")
assert df is None or df.iloc[0]['result'] in [None, float('inf'), float('nan')]
def test_sqrt_negative(self):
df, err = self.run_query("test_simple_math.csv",
"SELECT id, SQRT(a - d) as result FROM test_simple_math WHERE id = 1")
if df is not None:
assert pd.isna(df.iloc[0]['result']) or math.isnan(df.iloc[0]['result'])
def test_log_zero(self):
df, err = self.run_query("test_simple_math.csv",
"SELECT id, LOG10(a - a) as result FROM test_simple_math WHERE id = 1")
if df is not None:
result = df.iloc[0]['result']
assert pd.isna(result) or result == float('-inf')
if __name__ == "__main__":
pytest.main([__file__, "-v"])