Rename Limbo -> Turso in python tests

This commit is contained in:
PThorpe92 2025-06-30 22:11:10 -04:00
parent 5de904be47
commit 297cbbf726
No known key found for this signature in database
GPG key ID: D8D465987227968E
13 changed files with 122 additions and 121 deletions

View file

@ -35,13 +35,13 @@ You can freely write queries against these tables during compatibility testing.
For cases where output or behavior differs intentionally from SQLite (e.g. due to new features or limitations), tests should be placed in the testing/cli_tests/ directory and written in Python. For cases where output or behavior differs intentionally from SQLite (e.g. due to new features or limitations), tests should be placed in the testing/cli_tests/ directory and written in Python.
These tests use the TestLimboShell class: These tests use the TestTursoShell class:
```python ```python
from cli_tests.common import TestLimboShell from cli_tests.common import TestTursoShell
def test_uuid(): def test_uuid():
limbo = TestLimboShell() limbo = TestTursoShell()
limbo.run_test_fn("SELECT uuid4_str();", lambda res: len(res) == 36) limbo.run_test_fn("SELECT uuid4_str();", lambda res: len(res) == 36)
limbo.quit() limbo.quit()
``` ```

View file

@ -4,11 +4,11 @@ import time
from pathlib import Path from pathlib import Path
from cli_tests import console from cli_tests import console
from cli_tests.test_limbo_cli import TestLimboShell from cli_tests.test_turso_cli import TestTursoShell
def test_basic_queries(): def test_basic_queries():
shell = TestLimboShell() shell = TestTursoShell()
shell.run_test("select-1", "SELECT 1;", "1") shell.run_test("select-1", "SELECT 1;", "1")
shell.run_test("select-avg", "SELECT avg(age) FROM users;", "47.75") shell.run_test("select-avg", "SELECT avg(age) FROM users;", "47.75")
shell.run_test("select-sum", "SELECT sum(age) FROM users;", "191") shell.run_test("select-sum", "SELECT sum(age) FROM users;", "191")
@ -19,7 +19,7 @@ def test_basic_queries():
def test_schema_operations(): def test_schema_operations():
shell = TestLimboShell(init_blobs_table=True) shell = TestTursoShell(init_blobs_table=True)
expected = ( expected = (
"CREATE TABLE users (id INTEGER PRIMARY KEY, first_name TEXT, last_name TEXT, age INTEGER);\n" "CREATE TABLE users (id INTEGER PRIMARY KEY, first_name TEXT, last_name TEXT, age INTEGER);\n"
"CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT, price INTEGER);\n" "CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT, price INTEGER);\n"
@ -30,12 +30,12 @@ def test_schema_operations():
def test_file_operations(): def test_file_operations():
shell = TestLimboShell() shell = TestTursoShell()
shell.run_test("file-open", ".open testing/testing.db", "") shell.run_test("file-open", ".open testing/testing.db", "")
shell.run_test("file-users-count", "select count(*) from users;", "10000") shell.run_test("file-users-count", "select count(*) from users;", "10000")
shell.quit() shell.quit()
shell = TestLimboShell() shell = TestTursoShell()
shell.run_test("file-schema-1", ".open testing/testing.db", "") shell.run_test("file-schema-1", ".open testing/testing.db", "")
expected_user_schema = ( expected_user_schema = (
"CREATE TABLE users (\n" "CREATE TABLE users (\n"
@ -57,7 +57,7 @@ def test_file_operations():
def test_joins(): def test_joins():
shell = TestLimboShell() shell = TestTursoShell()
shell.run_test("open-file", ".open testing/testing.db", "") shell.run_test("open-file", ".open testing/testing.db", "")
shell.run_test("verify-tables", ".tables", "products users") shell.run_test("verify-tables", ".tables", "products users")
shell.run_test( shell.run_test(
@ -69,7 +69,7 @@ def test_joins():
def test_left_join_self(): def test_left_join_self():
shell = TestLimboShell( shell = TestTursoShell(
init_commands=""" init_commands="""
.open testing/testing.db .open testing/testing.db
""" """
@ -84,7 +84,7 @@ def test_left_join_self():
def test_where_clauses(): def test_where_clauses():
shell = TestLimboShell() shell = TestTursoShell()
shell.run_test("open-testing-db-file", ".open testing/testing.db", "") shell.run_test("open-testing-db-file", ".open testing/testing.db", "")
shell.run_test( shell.run_test(
"where-clause-eq-string", "where-clause-eq-string",
@ -95,7 +95,7 @@ def test_where_clauses():
def test_switch_back_to_in_memory(): def test_switch_back_to_in_memory():
shell = TestLimboShell() shell = TestTursoShell()
# First, open the file-based DB. # First, open the file-based DB.
shell.run_test("open-testing-db-file", ".open testing/testing.db", "") shell.run_test("open-testing-db-file", ".open testing/testing.db", "")
# Then switch back to :memory: # Then switch back to :memory:
@ -105,8 +105,8 @@ def test_switch_back_to_in_memory():
def test_verify_null_value(): def test_verify_null_value():
shell = TestLimboShell() shell = TestTursoShell()
shell.run_test("verify-null", "select NULL;", "LIMBO") shell.run_test("verify-null", "select NULL;", "TURSO")
shell.quit() shell.quit()
@ -118,8 +118,8 @@ def verify_output_file(filepath: Path, expected_lines: dict) -> None:
def test_output_file(): def test_output_file():
shell = TestLimboShell() shell = TestTursoShell()
output_filename = "limbo_output.txt" output_filename = "turso_output.txt"
output_file = shell.config.test_dir / shell.config.py_folder / output_filename output_file = shell.config.test_dir / shell.config.py_folder / output_filename
shell.execute_dot(".open testing/testing.db") shell.execute_dot(".open testing/testing.db")
@ -132,7 +132,7 @@ def test_output_file():
shell.execute_dot("SELECT 'TEST_ECHO';") shell.execute_dot("SELECT 'TEST_ECHO';")
shell.execute_dot("") shell.execute_dot("")
shell.execute_dot(".echo off") shell.execute_dot(".echo off")
shell.execute_dot(".nullvalue LIMBO") shell.execute_dot(".nullvalue turso")
shell.execute_dot(".show") shell.execute_dot(".show")
shell.execute_dot(".output stdout") shell.execute_dot(".output stdout")
time.sleep(3) time.sleep(3)
@ -146,21 +146,21 @@ def test_output_file():
"Error: pretty output can only be written to a tty": "Error message for pretty mode", "Error: pretty output can only be written to a tty": "Error message for pretty mode",
"SELECT 'TEST_ECHO'": "Echoed command", "SELECT 'TEST_ECHO'": "Echoed command",
"TEST_ECHO": "Echoed result", "TEST_ECHO": "Echoed result",
"Null value: LIMBO": "Null value setting", "Null value: turso": "Null value setting",
f"CWD: {shell.config.cwd}/{shell.config.test_dir}": "Working directory changed", f"CWD: {shell.config.cwd}/{shell.config.test_dir}": "Working directory changed",
"DB: testing/testing.db": "File database opened", "DB: testing/testing.db": "File database opened",
"Echo: off": "Echo turned off", "Echo: off": "Echo turned off",
} }
for line, _ in expected_lines.items(): for line, test in expected_lines.items():
assert line in contents, f"Expected line not found in file: {line}" assert line in contents, f"Expected line not found in file: {line} for {test}"
# Clean up # Clean up
os.remove(output_file) os.remove(output_file)
def test_multi_line_single_line_comments_succession(): def test_multi_line_single_line_comments_succession():
shell = TestLimboShell() shell = TestTursoShell()
comments = """-- First of the comments comments = """-- First of the comments
-- Second line of the comments -- Second line of the comments
SELECT 2;""" SELECT 2;"""
@ -169,7 +169,7 @@ SELECT 2;"""
def test_comments(): def test_comments():
shell = TestLimboShell() shell = TestTursoShell()
shell.run_test("single-line-comment", "-- this is a comment\nSELECT 1;", "1") shell.run_test("single-line-comment", "-- this is a comment\nSELECT 1;", "1")
shell.run_test("multi-line-comments", "-- First comment\n-- Second comment\nSELECT 2;", "2") shell.run_test("multi-line-comments", "-- First comment\n-- Second comment\nSELECT 2;", "2")
shell.run_test("block-comment", "/*\nMulti-line block comment\n*/\nSELECT 3;", "3") shell.run_test("block-comment", "/*\nMulti-line block comment\n*/\nSELECT 3;", "3")
@ -182,7 +182,7 @@ def test_comments():
def test_import_csv(): def test_import_csv():
shell = TestLimboShell() shell = TestTursoShell()
shell.run_test("memory-db", ".open :memory:", "") shell.run_test("memory-db", ".open :memory:", "")
shell.run_test("create-csv-table", "CREATE TABLE csv_table (c1 INT, c2 REAL, c3 String);", "") shell.run_test("create-csv-table", "CREATE TABLE csv_table (c1 INT, c2 REAL, c3 String);", "")
shell.run_test( shell.run_test(
@ -199,7 +199,7 @@ def test_import_csv():
def test_import_csv_verbose(): def test_import_csv_verbose():
shell = TestLimboShell() shell = TestTursoShell()
shell.run_test("open-memory", ".open :memory:", "") shell.run_test("open-memory", ".open :memory:", "")
shell.run_test("create-csv-table", "CREATE TABLE csv_table (c1 INT, c2 REAL, c3 String);", "") shell.run_test("create-csv-table", "CREATE TABLE csv_table (c1 INT, c2 REAL, c3 String);", "")
shell.run_test( shell.run_test(
@ -216,7 +216,7 @@ def test_import_csv_verbose():
def test_import_csv_skip(): def test_import_csv_skip():
shell = TestLimboShell() shell = TestTursoShell()
shell.run_test("open-memory", ".open :memory:", "") shell.run_test("open-memory", ".open :memory:", "")
shell.run_test("create-csv-table", "CREATE TABLE csv_table (c1 INT, c2 REAL, c3 String);", "") shell.run_test("create-csv-table", "CREATE TABLE csv_table (c1 INT, c2 REAL, c3 String);", "")
shell.run_test( shell.run_test(
@ -229,49 +229,49 @@ def test_import_csv_skip():
def test_table_patterns(): def test_table_patterns():
shell = TestLimboShell() shell = TestTursoShell()
shell.run_test("tables-pattern", ".tables us%", "users") shell.run_test("tables-pattern", ".tables us%", "users")
shell.quit() shell.quit()
def test_update_with_limit(): def test_update_with_limit():
limbo = TestLimboShell( turso = TestTursoShell(
"CREATE TABLE t (a,b,c); insert into t values (1,2,3), (4,5,6), (7,8,9), (1,2,3),(4,5,6), (7,8,9);" "CREATE TABLE t (a,b,c); insert into t values (1,2,3), (4,5,6), (7,8,9), (1,2,3),(4,5,6), (7,8,9);"
) )
limbo.run_test("update-limit", "UPDATE t SET a = 10 LIMIT 1;", "") turso.run_test("update-limit", "UPDATE t SET a = 10 LIMIT 1;", "")
limbo.run_test("update-limit-result", "SELECT COUNT(*) from t WHERE a = 10;", "1") turso.run_test("update-limit-result", "SELECT COUNT(*) from t WHERE a = 10;", "1")
limbo.run_test("update-limit-zero", "UPDATE t SET a = 100 LIMIT 0;", "") turso.run_test("update-limit-zero", "UPDATE t SET a = 100 LIMIT 0;", "")
limbo.run_test("update-limit-zero-result", "SELECT COUNT(*) from t WHERE a = 100;", "0") turso.run_test("update-limit-zero-result", "SELECT COUNT(*) from t WHERE a = 100;", "0")
limbo.run_test("update-limit-all", "UPDATE t SET a = 100 LIMIT -1;", "") turso.run_test("update-limit-all", "UPDATE t SET a = 100 LIMIT -1;", "")
# negative limit is treated as no limit in sqlite due to check for --val = 0 # negative limit is treated as no limit in sqlite due to check for --val = 0
limbo.run_test("update-limit-result", "SELECT COUNT(*) from t WHERE a = 100;", "6") turso.run_test("update-limit-result", "SELECT COUNT(*) from t WHERE a = 100;", "6")
limbo.run_test("udpate-limit-where", "UPDATE t SET a = 333 WHERE b = 5 LIMIT 1;", "") turso.run_test("udpate-limit-where", "UPDATE t SET a = 333 WHERE b = 5 LIMIT 1;", "")
limbo.run_test("update-limit-where-result", "SELECT COUNT(*) from t WHERE a = 333;", "1") turso.run_test("update-limit-where-result", "SELECT COUNT(*) from t WHERE a = 333;", "1")
limbo.quit() turso.quit()
def test_update_with_limit_and_offset(): def test_update_with_limit_and_offset():
limbo = TestLimboShell( turso = TestTursoShell(
"CREATE TABLE t (a,b,c); insert into t values (1,2,3), (4,5,6), (7,8,9), (1,2,3),(4,5,6), (7,8,9);" "CREATE TABLE t (a,b,c); insert into t values (1,2,3), (4,5,6), (7,8,9), (1,2,3),(4,5,6), (7,8,9);"
) )
limbo.run_test("update-limit-offset", "UPDATE t SET a = 10 LIMIT 1 OFFSET 3;", "") turso.run_test("update-limit-offset", "UPDATE t SET a = 10 LIMIT 1 OFFSET 3;", "")
limbo.run_test("update-limit-offset-result", "SELECT COUNT(*) from t WHERE a = 10;", "1") turso.run_test("update-limit-offset-result", "SELECT COUNT(*) from t WHERE a = 10;", "1")
limbo.run_test("update-limit-result", "SELECT a from t LIMIT 4;", "1\n4\n7\n10") turso.run_test("update-limit-result", "SELECT a from t LIMIT 4;", "1\n4\n7\n10")
limbo.run_test("update-limit-offset-zero", "UPDATE t SET a = 100 LIMIT 0 OFFSET 0;", "") turso.run_test("update-limit-offset-zero", "UPDATE t SET a = 100 LIMIT 0 OFFSET 0;", "")
limbo.run_test("update-limit-zero-result", "SELECT COUNT(*) from t WHERE a = 100;", "0") turso.run_test("update-limit-zero-result", "SELECT COUNT(*) from t WHERE a = 100;", "0")
limbo.run_test("update-limit-all", "UPDATE t SET a = 100 LIMIT -1 OFFSET 1;", "") turso.run_test("update-limit-all", "UPDATE t SET a = 100 LIMIT -1 OFFSET 1;", "")
limbo.run_test("update-limit-result", "SELECT COUNT(*) from t WHERE a = 100;", "5") turso.run_test("update-limit-result", "SELECT COUNT(*) from t WHERE a = 100;", "5")
limbo.run_test("udpate-limit-where", "UPDATE t SET a = 333 WHERE b = 5 LIMIT 1 OFFSET 2;", "") turso.run_test("udpate-limit-where", "UPDATE t SET a = 333 WHERE b = 5 LIMIT 1 OFFSET 2;", "")
limbo.run_test("update-limit-where-result", "SELECT COUNT(*) from t WHERE a = 333;", "0") turso.run_test("update-limit-where-result", "SELECT COUNT(*) from t WHERE a = 333;", "0")
limbo.quit() turso.quit()
def test_insert_default_values(): def test_insert_default_values():
limbo = TestLimboShell("CREATE TABLE t (a integer default(42),b integer default (43),c integer default(44));") turso = TestTursoShell("CREATE TABLE t (a integer default(42),b integer default (43),c integer default(44));")
for _ in range(1, 10): for _ in range(1, 10):
limbo.execute_dot("INSERT INTO t DEFAULT VALUES;") turso.execute_dot("INSERT INTO t DEFAULT VALUES;")
limbo.run_test("insert-default-values", "SELECT * FROM t;", "42|43|44\n" * 9) turso.run_test("insert-default-values", "SELECT * FROM t;", "42|43|44\n" * 9)
limbo.quit() turso.quit()
def test_uri_readonly(): def test_uri_readonly():
@ -289,7 +289,7 @@ def test_uri_readonly():
def main(): def main():
console.info("Running all Limbo CLI tests...") console.info("Running all turso CLI tests...")
test_basic_queries() test_basic_queries()
test_schema_operations() test_schema_operations()
test_file_operations() test_file_operations()

View file

@ -2,7 +2,7 @@
import os import os
from cli_tests import console from cli_tests import console
from cli_tests.test_limbo_cli import TestLimboShell from cli_tests.test_limbo_cli import TestTursoShell
from pydantic import BaseModel from pydantic import BaseModel
sqlite_flags = os.getenv("SQLITE_FLAGS", "-q").split(" ") sqlite_flags = os.getenv("SQLITE_FLAGS", "-q").split(" ")
@ -20,7 +20,7 @@ class CollateTest(BaseModel):
db_path: str = "testing/collate.db" db_path: str = "testing/collate.db"
def init_db(self): def init_db(self):
with TestLimboShell( with TestTursoShell(
init_commands="", init_commands="",
exec_name="sqlite3", exec_name="sqlite3",
flags=f"{self.db_path}", flags=f"{self.db_path}",
@ -41,7 +41,7 @@ class CollateTest(BaseModel):
f"{4}", f"{4}",
) )
def run(self, limbo: TestLimboShell): def run(self, limbo: TestTursoShell):
limbo.execute_dot(f".open {self.db_path}") limbo.execute_dot(f".open {self.db_path}")
limbo.run_test( limbo.run_test(
@ -130,7 +130,7 @@ def main():
try: try:
test.init_db() test.init_db()
# Use with syntax to automatically close shell on error # Use with syntax to automatically close shell on error
with TestLimboShell("") as limbo: with TestTursoShell("") as limbo:
test.run(limbo) test.run(limbo)
# test.test_compat() # test.test_compat()

View file

@ -8,7 +8,7 @@ import tempfile
from enum import Enum from enum import Enum
from cli_tests import console from cli_tests import console
from cli_tests.test_limbo_cli import TestLimboShell from cli_tests.test_limbo_cli import TestTursoShell
from faker import Faker from faker import Faker
from faker.providers.lorem.en_US import Provider as P from faker.providers.lorem.en_US import Provider as P
from pydantic import BaseModel from pydantic import BaseModel
@ -248,7 +248,7 @@ class ConstraintTest(BaseModel):
def run( def run(
self, self,
limbo: TestLimboShell, limbo: TestTursoShell,
): ):
big_stmt = [self.table.create_table()] big_stmt = [self.table.create_table()]
for insert_stmt in self.insert_stmts: for insert_stmt in self.insert_stmts:
@ -333,7 +333,7 @@ def custom_test_1() -> ConstraintTest:
) )
def custom_test_2(limbo: TestLimboShell): def custom_test_2(limbo: TestTursoShell):
create = "CREATE TABLE users (id INT PRIMARY KEY, username TEXT);" create = "CREATE TABLE users (id INT PRIMARY KEY, username TEXT);"
first_insert = "INSERT INTO users VALUES (1, 'alice');" first_insert = "INSERT INTO users VALUES (1, 'alice');"
limbo.run_test("Create unique INT index", create + first_insert, "") limbo.run_test("Create unique INT index", create + first_insert, "")
@ -345,7 +345,7 @@ def custom_test_2(limbo: TestLimboShell):
# Issue #1482 # Issue #1482
def regression_test_update_single_key(limbo: TestLimboShell): def regression_test_update_single_key(limbo: TestTursoShell):
create = "CREATE TABLE t(a unique);" create = "CREATE TABLE t(a unique);"
first_insert = "INSERT INTO t VALUES (1);" first_insert = "INSERT INTO t VALUES (1);"
limbo.run_test("Create simple table with 1 unique value", create + first_insert, "") limbo.run_test("Create simple table with 1 unique value", create + first_insert, "")
@ -374,7 +374,7 @@ def main():
with tempfile.NamedTemporaryFile(suffix=".db") as tmp: with tempfile.NamedTemporaryFile(suffix=".db") as tmp:
try: try:
# Use with syntax to automatically close shell on error # Use with syntax to automatically close shell on error
with TestLimboShell("") as limbo: with TestTursoShell("") as limbo:
limbo.execute_dot(f".open {tmp.name}") limbo.execute_dot(f".open {tmp.name}")
test.run(limbo) test.run(limbo)
except Exception as e: except Exception as e:
@ -386,7 +386,7 @@ def main():
for test in tests: for test in tests:
with tempfile.NamedTemporaryFile(suffix=".db") as tmp: with tempfile.NamedTemporaryFile(suffix=".db") as tmp:
try: try:
with TestLimboShell("") as limbo: with TestTursoShell("") as limbo:
limbo.execute_dot(f".open {tmp.name}") limbo.execute_dot(f".open {tmp.name}")
test(limbo) test(limbo)
except Exception as e: except Exception as e:

View file

@ -2,7 +2,7 @@
import os import os
from cli_tests import console from cli_tests import console
from cli_tests.test_limbo_cli import TestLimboShell from cli_tests.test_limbo_cli import TestTursoShell
sqlite_exec = "./scripts/limbo-sqlite3" sqlite_exec = "./scripts/limbo-sqlite3"
sqlite_flags = os.getenv("SQLITE_FLAGS", "-q").split(" ") sqlite_flags = os.getenv("SQLITE_FLAGS", "-q").split(" ")
@ -31,7 +31,7 @@ def validate_string_uuid(res):
def test_uuid(): def test_uuid():
limbo = TestLimboShell() limbo = TestTursoShell()
specific_time = "01945ca0-3189-76c0-9a8f-caf310fc8b8e" specific_time = "01945ca0-3189-76c0-9a8f-caf310fc8b8e"
# these are built into the binary, so we just test they work # these are built into the binary, so we just test they work
limbo.run_test_fn( limbo.run_test_fn(
@ -71,7 +71,7 @@ def null(res):
def test_regexp(): def test_regexp():
limbo = TestLimboShell(test_data) limbo = TestTursoShell(test_data)
extension_path = "./target/debug/liblimbo_regexp" extension_path = "./target/debug/liblimbo_regexp"
# before extension loads, assert no function # before extension loads, assert no function
limbo.run_test_fn( limbo.run_test_fn(
@ -127,7 +127,7 @@ def validate_percentile_disc(res):
def test_aggregates(): def test_aggregates():
limbo = TestLimboShell(init_commands=test_data) limbo = TestTursoShell(init_commands=test_data)
extension_path = "./target/debug/liblimbo_percentile" extension_path = "./target/debug/liblimbo_percentile"
# assert no function before extension loads # assert no function before extension loads
limbo.run_test_fn( limbo.run_test_fn(
@ -204,7 +204,7 @@ def validate_base64_decode(a):
def test_crypto(): def test_crypto():
limbo = TestLimboShell() limbo = TestTursoShell()
extension_path = "./target/debug/liblimbo_crypto" extension_path = "./target/debug/liblimbo_crypto"
# assert no function before extension loads # assert no function before extension loads
limbo.run_test_fn( limbo.run_test_fn(
@ -303,15 +303,15 @@ def test_crypto():
def test_series(): def test_series():
console.info("Running test_series for Limbo") console.info("Running test_series for Limbo")
limbo = TestLimboShell() limbo = TestTursoShell()
_test_series(limbo) _test_series(limbo)
console.info("Running test_series for SQLite") console.info("Running test_series for SQLite")
limbo = TestLimboShell(exec_name="sqlite3") limbo = TestTursoShell(exec_name="sqlite3")
_test_series(limbo) _test_series(limbo)
def _test_series(limbo: TestLimboShell): def _test_series(limbo: TestTursoShell):
limbo.run_test_fn( limbo.run_test_fn(
"SELECT * FROM generate_series(1, 10);", "SELECT * FROM generate_series(1, 10);",
lambda res: res == "1\n2\n3\n4\n5\n6\n7\n8\n9\n10", lambda res: res == "1\n2\n3\n4\n5\n6\n7\n8\n9\n10",
@ -345,7 +345,7 @@ def test_kv():
def _test_kv(exec_name, ext_path): def _test_kv(exec_name, ext_path):
console.info(f"Running test_kv for {ext_path}") console.info(f"Running test_kv for {ext_path}")
limbo = TestLimboShell( limbo = TestTursoShell(
exec_name=exec_name, exec_name=exec_name,
) )
# first, create a normal table to ensure no issues # first, create a normal table to ensure no issues
@ -436,7 +436,7 @@ def _test_kv(exec_name, ext_path):
def test_ipaddr(): def test_ipaddr():
limbo = TestLimboShell() limbo = TestTursoShell()
ext_path = "./target/debug/liblimbo_ipaddr" ext_path = "./target/debug/liblimbo_ipaddr"
limbo.run_test_fn( limbo.run_test_fn(
@ -504,7 +504,7 @@ def test_ipaddr():
def test_vfs(): def test_vfs():
limbo = TestLimboShell() limbo = TestTursoShell()
ext_path = "target/debug/libturso_ext_tests" ext_path = "target/debug/libturso_ext_tests"
limbo.run_test_fn(".vfslist", lambda x: "testvfs" not in x, "testvfs not loaded") limbo.run_test_fn(".vfslist", lambda x: "testvfs" not in x, "testvfs not loaded")
limbo.execute_dot(f".load {ext_path}") limbo.execute_dot(f".load {ext_path}")
@ -531,7 +531,7 @@ def test_vfs():
def test_drop_virtual_table(): def test_drop_virtual_table():
ext_path = "target/debug/libturso_ext_tests" ext_path = "target/debug/libturso_ext_tests"
limbo = TestLimboShell() limbo = TestTursoShell()
limbo.execute_dot(f".load {ext_path}") limbo.execute_dot(f".load {ext_path}")
limbo.run_debug( limbo.run_debug(
"create virtual table t using kv_store;", "create virtual table t using kv_store;",
@ -556,7 +556,7 @@ def test_drop_virtual_table():
def test_sqlite_vfs_compat(): def test_sqlite_vfs_compat():
sqlite = TestLimboShell( sqlite = TestTursoShell(
init_commands="", init_commands="",
exec_name="sqlite3", exec_name="sqlite3",
flags="testing/vfs.db", flags="testing/vfs.db",
@ -587,7 +587,7 @@ def test_sqlite_vfs_compat():
def test_create_virtual_table(): def test_create_virtual_table():
ext_path = "target/debug/libturso_ext_tests" ext_path = "target/debug/libturso_ext_tests"
limbo = TestLimboShell() limbo = TestTursoShell()
limbo.execute_dot(f".load {ext_path}") limbo.execute_dot(f".load {ext_path}")
limbo.run_debug("CREATE VIRTUAL TABLE t1 USING kv_store;") limbo.run_debug("CREATE VIRTUAL TABLE t1 USING kv_store;")
@ -625,7 +625,7 @@ def test_create_virtual_table():
def test_csv(): def test_csv():
limbo = TestLimboShell() limbo = TestTursoShell()
ext_path = "./target/debug/liblimbo_csv" ext_path = "./target/debug/liblimbo_csv"
limbo.execute_dot(f".load {ext_path}") limbo.execute_dot(f".load {ext_path}")
@ -712,7 +712,7 @@ def cleanup():
def test_tablestats(): def test_tablestats():
ext_path = "target/debug/libturso_ext_tests" ext_path = "target/debug/libturso_ext_tests"
limbo = TestLimboShell(use_testing_db=True) limbo = TestTursoShell(use_testing_db=True)
limbo.execute_dot("CREATE TABLE people(id INTEGER PRIMARY KEY, name TEXT);") limbo.execute_dot("CREATE TABLE people(id INTEGER PRIMARY KEY, name TEXT);")
limbo.execute_dot("INSERT INTO people(name) VALUES ('Ada'), ('Grace'), ('Linus');") limbo.execute_dot("INSERT INTO people(name) VALUES ('Ada'), ('Grace'), ('Linus');")

View file

@ -2,7 +2,7 @@
import os import os
from cli_tests import console from cli_tests import console
from cli_tests.test_limbo_cli import TestLimboShell from cli_tests.test_limbo_cli import TestTursoShell
sqlite_flags = os.getenv("SQLITE_FLAGS", "-q").split(" ") sqlite_flags = os.getenv("SQLITE_FLAGS", "-q").split(" ")
@ -12,7 +12,7 @@ def validate_with_expected(result: str, expected: str):
def stub_memory_test( def stub_memory_test(
limbo: TestLimboShell, limbo: TestTursoShell,
name: str, name: str,
blob_size: int = 1024**2, blob_size: int = 1024**2,
vals: int = 100, vals: int = 100,
@ -106,7 +106,7 @@ def main():
# TODO see how to parallelize this loop with different subprocesses # TODO see how to parallelize this loop with different subprocesses
for test in tests: for test in tests:
try: try:
with TestLimboShell("") as limbo: with TestTursoShell("") as limbo:
stub_memory_test(limbo, **test) stub_memory_test(limbo, **test)
except Exception as e: except Exception as e:
console.error(f"Test FAILED: {e}") console.error(f"Test FAILED: {e}")

View file

@ -4,6 +4,7 @@ import select
import subprocess import subprocess
from pathlib import Path from pathlib import Path
from time import sleep from time import sleep
import time
from typing import Callable, List, Optional from typing import Callable, List, Optional
from cli_tests import console from cli_tests import console
@ -21,7 +22,7 @@ class ShellConfig:
self.test_files: Path = Path("test_files") self.test_files: Path = Path("test_files")
class LimboShell: class TursoShell:
def __init__(self, config: ShellConfig, init_commands: Optional[str] = None): def __init__(self, config: ShellConfig, init_commands: Optional[str] = None):
self.config = config self.config = config
self.pipe = self._start_repl(init_commands) self.pipe = self._start_repl(init_commands)
@ -65,7 +66,7 @@ class LimboShell:
fragment = self.pipe.stderr.read(PIPE_BUF).decode() fragment = self.pipe.stderr.read(PIPE_BUF).decode()
if not fragment: if not fragment:
console.error(output, end="", _stack_offset=2) console.error(output, end="", _stack_offset=2)
raise RuntimeError("Error encountered in Limbo shell.") raise RuntimeError("Error encountered in Turso shell.")
output += fragment output += fragment
if self.pipe.stdout in ready_or_errors: if self.pipe.stdout in ready_or_errors:
fragment = self.pipe.stdout.read(PIPE_BUF).decode() fragment = self.pipe.stdout.read(PIPE_BUF).decode()
@ -76,7 +77,7 @@ class LimboShell:
def _write_to_pipe(self, command: str) -> None: def _write_to_pipe(self, command: str) -> None:
if not self.pipe.stdin: if not self.pipe.stdin:
raise RuntimeError("Failed to start Limbo REPL") raise RuntimeError("Failed to start Turso REPL")
self.pipe.stdin.write((command + "\n").encode()) self.pipe.stdin.write((command + "\n").encode())
self.pipe.stdin.flush() self.pipe.stdin.flush()
@ -93,7 +94,7 @@ class LimboShell:
self.pipe.kill() self.pipe.kill()
class TestLimboShell: class TestTursoShell:
def __init__( def __init__(
self, self,
init_commands: Optional[str] = None, init_commands: Optional[str] = None,
@ -103,9 +104,7 @@ class TestLimboShell:
flags="", flags="",
): ):
if exec_name is None: if exec_name is None:
exec_name = os.environ.get("SQLITE_EXEC") exec_name = os.environ.get("SQLITE_EXEC", "./scripts/limbo-sqlite3")
if exec_name is None:
exec_name = "./scripts/limbo-sqlite3"
if flags == "": if flags == "":
flags = "-q" flags = "-q"
self.config = ShellConfig(exe_name=exec_name, flags=flags) self.config = ShellConfig(exe_name=exec_name, flags=flags)
@ -127,11 +126,12 @@ INSERT INTO products VALUES (1, 'Hat', 19.99), (2, 'Shirt', 29.99),
CREATE TABLE t (x1, x2, x3, x4); CREATE TABLE t (x1, x2, x3, x4);
INSERT INTO t VALUES (zeroblob(1024 - 1), zeroblob(1024 - 2), zeroblob(1024 - 3), zeroblob(1024 - 4));""" INSERT INTO t VALUES (zeroblob(1024 - 1), zeroblob(1024 - 2), zeroblob(1024 - 3), zeroblob(1024 - 4));"""
init_commands += "\n.nullvalue LIMBO" init_commands += "\n.nullvalue TURSO"
self.shell = LimboShell(self.config, init_commands) self.shell = TursoShell(self.config, init_commands)
def quit(self): def quit(self, cleanup=True):
self.cleanup_test_db() if cleanup:
self.cleanup_test_db()
self.shell.quit() self.shell.quit()
def run_test(self, name: str, sql: str, expected: str) -> None: def run_test(self, name: str, sql: str, expected: str) -> None:
@ -162,6 +162,7 @@ INSERT INTO t VALUES (zeroblob(1024 - 1), zeroblob(1024 - 2), zeroblob(1024 - 3)
path = os.path.join("testing", "testing_clone.db") path = os.path.join("testing", "testing_clone.db")
if os.path.exists(path): if os.path.exists(path):
os.remove(path) os.remove(path)
time.sleep(0.1) # Ensure the file is removed before cloning
cmd = "sqlite3 testing/testing.db '.clone testing/testing_clone.db'" cmd = "sqlite3 testing/testing.db '.clone testing/testing_clone.db'"
subprocess.run(cmd, shell=True, capture_output=True, text=True) subprocess.run(cmd, shell=True, capture_output=True, text=True)
if not os.path.exists("testing/testing_clone.db"): if not os.path.exists("testing/testing_clone.db"):

View file

@ -2,7 +2,7 @@
import os import os
from cli_tests import console from cli_tests import console
from cli_tests.test_limbo_cli import TestLimboShell from cli_tests.test_limbo_cli import TestTursoShell
from pydantic import BaseModel from pydantic import BaseModel
sqlite_flags = os.getenv("SQLITE_FLAGS", "-q").split(" ") sqlite_flags = os.getenv("SQLITE_FLAGS", "-q").split(" ")
@ -17,7 +17,7 @@ class UpdateTest(BaseModel):
db_path: str = "testing/update.db" db_path: str = "testing/update.db"
def init_db(self): def init_db(self):
with TestLimboShell( with TestTursoShell(
init_commands="", init_commands="",
exec_name="sqlite3", exec_name="sqlite3",
flags=f"{self.db_path}", flags=f"{self.db_path}",
@ -48,7 +48,7 @@ class UpdateTest(BaseModel):
"\n".join(expected), "\n".join(expected),
) )
def run(self, limbo: TestLimboShell): def run(self, limbo: TestTursoShell):
limbo.execute_dot(f".open {self.db_path}") limbo.execute_dot(f".open {self.db_path}")
# TODO blobs are hard. Forget about blob updates for now # TODO blobs are hard. Forget about blob updates for now
# one_blob = ("0" * ((self.blob_size * 2) - 1)) + "1" # one_blob = ("0" * ((self.blob_size * 2) - 1)) + "1"
@ -61,7 +61,7 @@ class UpdateTest(BaseModel):
def test_compat(self): def test_compat(self):
console.info("Testing in SQLite\n") console.info("Testing in SQLite\n")
with TestLimboShell( with TestTursoShell(
init_commands="", init_commands="",
exec_name="sqlite3", exec_name="sqlite3",
flags=f"{self.db_path}", flags=f"{self.db_path}",
@ -112,7 +112,7 @@ def main():
try: try:
test.init_db() test.init_db()
# Use with syntax to automatically close shell on error # Use with syntax to automatically close shell on error
with TestLimboShell("") as limbo: with TestTursoShell("") as limbo:
test.run(limbo) test.run(limbo)
test.test_compat() test.test_compat()

View file

@ -10,7 +10,7 @@ from time import perf_counter, sleep
from typing import Dict from typing import Dict
from cli_tests.console import error, info, test from cli_tests.console import error, info, test
from cli_tests.test_limbo_cli import TestLimboShell from cli_tests.test_limbo_cli import TestTursoShell
LIMBO_BIN = Path("./target/release/limbo") LIMBO_BIN = Path("./target/release/limbo")
DB_FILE = Path("testing/temp.db") DB_FILE = Path("testing/temp.db")
@ -27,7 +27,7 @@ def bench_one(vfs: str, sql: str, iterations: int) -> list[float]:
Launch a single Limbo process with the requested VFS, run `sql` Launch a single Limbo process with the requested VFS, run `sql`
`iterations` times, return a list of elapsed wallclock times. `iterations` times, return a list of elapsed wallclock times.
""" """
shell = TestLimboShell( shell = TestTursoShell(
exec_name=str(LIMBO_BIN), exec_name=str(LIMBO_BIN),
flags=f"-q -m list --vfs {vfs} {DB_FILE}", flags=f"-q -m list --vfs {vfs} {DB_FILE}",
init_commands="", init_commands="",

View file

@ -4,7 +4,7 @@ import tempfile
from time import sleep from time import sleep
from cli_tests import console from cli_tests import console
from cli_tests.test_limbo_cli import TestLimboShell from cli_tests.test_limbo_cli import TestTursoShell
from pydantic import BaseModel from pydantic import BaseModel
sqlite_flags = os.getenv("SQLITE_FLAGS", "-q").split(" ") sqlite_flags = os.getenv("SQLITE_FLAGS", "-q").split(" ")
@ -18,7 +18,7 @@ class InsertTest(BaseModel):
has_blob: bool = True has_blob: bool = True
db_path: str = "" db_path: str = ""
def run(self, limbo: TestLimboShell): def run(self, limbo: TestTursoShell):
zero_blob = "0" * self.blob_size * 2 zero_blob = "0" * self.blob_size * 2
big_stmt = [self.db_schema, "CREATE INDEX test_index ON test(t1);"] big_stmt = [self.db_schema, "CREATE INDEX test_index ON test(t1);"]
big_stmt = big_stmt + [ big_stmt = big_stmt + [
@ -51,7 +51,7 @@ class InsertTest(BaseModel):
def test_compat(self): def test_compat(self):
console.info("Testing in SQLite\n") console.info("Testing in SQLite\n")
with TestLimboShell( with TestTursoShell(
init_commands="", init_commands="",
exec_name="sqlite3", exec_name="sqlite3",
flags=f"{self.db_path}", flags=f"{self.db_path}",
@ -148,7 +148,7 @@ def main():
test.db_path = tmp.name test.db_path = tmp.name
try: try:
# Use with syntax to automatically close shell on error # Use with syntax to automatically close shell on error
with TestLimboShell("") as limbo: with TestTursoShell("") as limbo:
limbo.execute_dot(f".open {test.db_path}") limbo.execute_dot(f".open {test.db_path}")
test.run(limbo) test.run(limbo)
sleep(0.3) sleep(0.3)

View file

@ -1,6 +1,6 @@
[project] [project]
description = "Limbo Python Testing Project" description = "Turso Python Testing Project"
name = "limbo_test" name = "turso_test"
readme = "README.md" readme = "README.md"
requires-python = ">=3.13" requires-python = ">=3.13"
version = "0.1.0" version = "0.1.0"

Binary file not shown.

32
uv.lock generated
View file

@ -6,9 +6,9 @@ requires-python = ">=3.13"
members = [ members = [
"antithesis-tests", "antithesis-tests",
"limbo", "limbo",
"limbo-test",
"pyturso", "pyturso",
"scripts", "scripts",
"turso-test",
] ]
[[package]] [[package]]
@ -225,21 +225,6 @@ dependencies = [
[package.metadata] [package.metadata]
requires-dist = [{ name = "rich", specifier = ">=14.0.0" }] requires-dist = [{ name = "rich", specifier = ">=14.0.0" }]
[[package]]
name = "limbo-test"
version = "0.1.0"
source = { editable = "testing" }
dependencies = [
{ name = "faker" },
{ name = "pydantic" },
]
[package.metadata]
requires-dist = [
{ name = "faker", specifier = ">=37.1.0" },
{ name = "pydantic", specifier = ">=2.11.1" },
]
[[package]] [[package]]
name = "markdown-it-py" name = "markdown-it-py"
version = "3.0.0" version = "3.0.0"
@ -582,6 +567,21 @@ dependencies = [
[package.metadata] [package.metadata]
requires-dist = [{ name = "pygithub", specifier = ">=2.6.1" }] requires-dist = [{ name = "pygithub", specifier = ">=2.6.1" }]
[[package]]
name = "turso-test"
version = "0.1.0"
source = { editable = "testing" }
dependencies = [
{ name = "faker" },
{ name = "pydantic" },
]
[package.metadata]
requires-dist = [
{ name = "faker", specifier = ">=37.1.0" },
{ name = "pydantic", specifier = ">=2.11.1" },
]
[[package]] [[package]]
name = "typing-extensions" name = "typing-extensions"
version = "4.13.0" version = "4.13.0"