From bdef83dc1cee58b255beda0f1b1627602e44c305 Mon Sep 17 00:00:00 2001 From: pedrocarlo Date: Thu, 3 Apr 2025 18:43:19 -0300 Subject: [PATCH] update test --- Makefile | 12 +- testing/cli_tests/update.py | 135 ++++++++++++++++++++++ testing/cli_tests/{writes.py => write.py} | 36 ++---- testing/pyproject.toml | 3 +- 4 files changed, 152 insertions(+), 34 deletions(-) create mode 100644 testing/cli_tests/update.py rename testing/cli_tests/{writes.py => write.py} (77%) diff --git a/Makefile b/Makefile index 3357ff76d..2d0924840 100644 --- a/Makefile +++ b/Makefile @@ -66,7 +66,7 @@ uv-sync: uv sync --all-packages .PHONE: uv-sync -test: limbo uv-sync test-compat test-vector test-sqlite3 test-shell test-extensions test-memory test-writes +test: limbo uv-sync test-compat test-vector test-sqlite3 test-shell test-extensions test-memory test-write test-update .PHONY: test test-extensions: limbo uv-sync @@ -102,9 +102,13 @@ test-memory: SQLITE_EXEC=$(SQLITE_EXEC) ./testing/cli_tests/memory.py .PHONY: test-memory -test-writes: limbo uv-sync - SQLITE_EXEC=$(SQLITE_EXEC) uv run --project limbo_test test-writes -.PHONY: test-writes +test-write: limbo uv-sync + SQLITE_EXEC=$(SQLITE_EXEC) uv run --project limbo_test test-write +.PHONY: test-write + +test-update: limbo uv-sync + SQLITE_EXEC=$(SQLITE_EXEC) uv run --project limbo_test test-update +.PHONY: test-update clickbench: ./perf/clickbench/benchmark.sh diff --git a/testing/cli_tests/update.py b/testing/cli_tests/update.py new file mode 100644 index 000000000..e0473c877 --- /dev/null +++ b/testing/cli_tests/update.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 +import os +from cli_tests.test_limbo_cli import TestLimboShell +from pydantic import BaseModel + + +sqlite_flags = os.getenv("SQLITE_FLAGS", "-q").split(" ") + + +class UpdateTest(BaseModel): + name: str + db_schema: str = "CREATE TABLE test (key INTEGER, t1 BLOB, t2 INTEGER, t3 TEXT);" + blob_size: int = 1024 + vals: int = 1000 + updates: int = 1 + db_path: str = "testing/update.db" + + def init_db(self): + with TestLimboShell( + init_commands="", + exec_name="sqlite3", + flags=f"{self.db_path}", + ) as sqlite: + sqlite.execute_dot(f".open {self.db_path}") + zero_blob = "0" * self.blob_size * 2 + t2_val = "1" + t3_val = "2" + stmt = [self.db_schema] + stmt = stmt + [ + f"INSERT INTO test (key, t1, t2, t3) VALUES ({i} ,zeroblob({self.blob_size}), {t2_val}, {t3_val});" + for i in range(self.vals) + ] + stmt.append("SELECT count(*) FROM test;") + + sqlite.run_test( + "Init Update Db in Sqlite", + "".join(stmt), + f"{self.vals}", + ) + + stmt = [ + f"SELECT hex(t1), t2, t3 FROM test LIMIT 1 OFFSET {i};" + for i in range(self.vals) + ] + + expected = [f"{zero_blob}|{t2_val}|{t3_val}" for _ in range(self.vals)] + sqlite.run_test( + "Check Values correctly inserted in Sqlite", + "".join(stmt), + "\n".join(expected), + ) + + def run(self, limbo: TestLimboShell): + limbo.execute_dot(f".open {self.db_path}") + # TODO blobs are hard. Forget about blob updates for now + # one_blob = ("0" * ((self.blob_size * 2) - 1)) + "1" + # TODO For now update just on one row. To expand the tests in the future + # use self.updates and do more than 1 update + t2_update_val = "123" + stmt = f"UPDATE test SET t2 = {t2_update_val} WHERE key = {0};" + limbo.run_test(self.name, stmt, "") + + def test_compat(self): + print("Testing in SQLite\n") + + with TestLimboShell( + init_commands="", + exec_name="sqlite3", + flags=f"{self.db_path}", + ) as sqlite: + sqlite.execute_dot(f".open {self.db_path}") + zero_blob = "0" * self.blob_size * 2 + + t2_val = "1" + t2_update_val = "123" + t3_val = "2" + stmt = [] + stmt.append("SELECT count(*) FROM test;") + + sqlite.run_test( + "Check all rows present in Sqlite", + "".join(stmt), + f"{self.vals}", + ) + + stmt = [ + f"SELECT hex(t1), t2, t3 FROM test LIMIT 1 OFFSET {i};" + for i in range(self.vals) + ] + + expected = [ + f"{zero_blob}|{t2_val}|{t3_val}" + if i != 0 + else f"{zero_blob}|{t2_update_val}|{t3_val}" + for i in range(self.vals) + ] + sqlite.run_test( + "Check Values correctly updated in Sqlite", + "".join(stmt), + "\n".join(expected), + ) + print() + + +def cleanup(db_fullpath: str): + wal_path = f"{db_fullpath}-wal" + shm_path = f"{db_fullpath}-shm" + paths = [db_fullpath, wal_path, shm_path] + for path in paths: + if os.path.exists(path): + os.remove(path) + + +def main(): + test = UpdateTest(name="Update 1 column", vals=1) + db_path = test.db_path + try: + test.init_db() + # Use with syntax to automatically close shell on error + with TestLimboShell("") as limbo: + test.run(limbo) + + test.test_compat() + + except Exception as e: + print(f"Test FAILED: {e}") + cleanup(db_path) + exit(1) + # delete db after every compat test so we we have fresh db for next test + cleanup(db_path) + print("All tests passed successfully.") + + +if __name__ == "__main__": + main() diff --git a/testing/cli_tests/writes.py b/testing/cli_tests/write.py similarity index 77% rename from testing/cli_tests/writes.py rename to testing/cli_tests/write.py index a79b44448..4ccf1bc0c 100755 --- a/testing/cli_tests/writes.py +++ b/testing/cli_tests/write.py @@ -61,12 +61,12 @@ class InsertTest(BaseModel): lambda res: self.db_schema in res, "Tables created by previous Limbo test exist in db file", ) - # TODO Have some pydantic object be passed to this function with common fields - # To extract the information necessary to query the db in sqlite - # The object should contain Schema information and queries that should be run to - # test in sqlite for compatibility sakes + sqlite.run_test_fn( + "SELECT count(*) FROM test;", + lambda res: res == str(self.vals * 2), + "Counting total rows inserted", + ) print() - pass def validate_with_expected(result: str, expected: str): @@ -75,7 +75,7 @@ def validate_with_expected(result: str, expected: str): # TODO no delete tests for now def blob_tests() -> list[InsertTest]: - tests: list[dict] = [] + tests: list[InsertTest] = [] for vals in range(0, 1000, 100): tests.append( @@ -121,28 +121,6 @@ def blob_tests() -> list[InsertTest]: return tests -def test_sqlite_compat(db_fullpath: str, schema: str): - with TestLimboShell( - init_commands="", - exec_name="sqlite3", - flags=f"{db_fullpath}", - ) as sqlite: - sqlite.run_test_fn( - ".show", - lambda res: f"filename: {db_fullpath}" in res, - "Opened db file created with Limbo in sqlite3", - ) - sqlite.run_test_fn( - ".schema", - lambda res: schema in res, - "Tables created by previous Limbo test exist in db file", - ) - # TODO Have some pydantic object be passed to this function with common fields - # To extract the information necessary to query the db in sqlite - # The object should contain Schema information and queries that should be run to - # test in sqlite for compatibility sakes - - def cleanup(db_fullpath: str): wal_path = f"{db_fullpath}-wal" shm_path = f"{db_fullpath}-shm" @@ -158,7 +136,7 @@ def main(): db_path = test.db_path try: # Use with syntax to automatically close shell on error - with TestLimboShell() as limbo: + with TestLimboShell("") as limbo: limbo.execute_dot(f".open {db_path}") test.run(limbo) diff --git a/testing/pyproject.toml b/testing/pyproject.toml index 548c4ab81..d4e257361 100644 --- a/testing/pyproject.toml +++ b/testing/pyproject.toml @@ -10,9 +10,10 @@ dependencies = [ ] [project.scripts] -test-writes = "cli_tests.writes:main" +test-write = "cli_tests.write:main" test-shell = "cli_tests.cli_test_cases:main" test-extensions = "cli_tests.extensions:main" +test-update = "cli_tests.update:main" [tool.uv] package = true