mirror of
https://github.com/aljazceru/turso.git
synced 2026-01-21 09:04:19 +01:00
Merge with main
This commit is contained in:
139
testing/extensions.py
Executable file
139
testing/extensions.py
Executable file
@@ -0,0 +1,139 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import subprocess
|
||||
import select
|
||||
import time
|
||||
import uuid
|
||||
|
||||
sqlite_exec = "./target/debug/limbo"
|
||||
sqlite_flags = os.getenv("SQLITE_FLAGS", "-q").split(" ")
|
||||
|
||||
|
||||
def init_limbo():
|
||||
pipe = subprocess.Popen(
|
||||
[sqlite_exec, *sqlite_flags],
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
bufsize=0,
|
||||
)
|
||||
return pipe
|
||||
|
||||
|
||||
def execute_sql(pipe, sql):
|
||||
end_suffix = "END_OF_RESULT"
|
||||
write_to_pipe(pipe, sql)
|
||||
write_to_pipe(pipe, f"SELECT '{end_suffix}';\n")
|
||||
stdout = pipe.stdout
|
||||
stderr = pipe.stderr
|
||||
output = ""
|
||||
while True:
|
||||
ready_to_read, _, error_in_pipe = select.select(
|
||||
[stdout, stderr], [], [stdout, stderr]
|
||||
)
|
||||
ready_to_read_or_err = set(ready_to_read + error_in_pipe)
|
||||
if stderr in ready_to_read_or_err:
|
||||
exit_on_error(stderr)
|
||||
|
||||
if stdout in ready_to_read_or_err:
|
||||
fragment = stdout.read(select.PIPE_BUF)
|
||||
output += fragment.decode()
|
||||
if output.rstrip().endswith(end_suffix):
|
||||
output = output.rstrip().removesuffix(end_suffix)
|
||||
break
|
||||
output = strip_each_line(output)
|
||||
return output
|
||||
|
||||
|
||||
def strip_each_line(lines: str) -> str:
|
||||
lines = lines.split("\n")
|
||||
lines = [line.strip() for line in lines if line != ""]
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def write_to_pipe(pipe, command):
|
||||
if pipe.stdin is None:
|
||||
raise RuntimeError("Failed to write to shell")
|
||||
pipe.stdin.write((command + "\n").encode())
|
||||
pipe.stdin.flush()
|
||||
|
||||
|
||||
def exit_on_error(stderr):
|
||||
while True:
|
||||
ready_to_read, _, _ = select.select([stderr], [], [])
|
||||
if not ready_to_read:
|
||||
break
|
||||
print(stderr.read().decode(), end="")
|
||||
exit(1)
|
||||
|
||||
|
||||
def run_test(pipe, sql, validator=None):
|
||||
print(f"Running test: {sql}")
|
||||
result = execute_sql(pipe, sql)
|
||||
if validator is not None:
|
||||
if not validator(result):
|
||||
print(f"Test FAILED: {sql}")
|
||||
print(f"Returned: {result}")
|
||||
raise Exception("Validation failed")
|
||||
print("Test PASSED")
|
||||
|
||||
|
||||
def validate_blob(result):
|
||||
# HACK: blobs are difficult to test because the shell
|
||||
# tries to return them as utf8 strings, so we call hex
|
||||
# and assert they are valid hex digits
|
||||
return int(result, 16) is not None
|
||||
|
||||
|
||||
def validate_string_uuid(result):
|
||||
return len(result) == 36 and result.count("-") == 4
|
||||
|
||||
|
||||
def returns_null(result):
|
||||
return result == "" or result == b"\n" or result == b""
|
||||
|
||||
|
||||
def assert_now_unixtime(result):
|
||||
return result == str(int(time.time()))
|
||||
|
||||
|
||||
def assert_specific_time(result):
|
||||
return result == "1736720789"
|
||||
|
||||
|
||||
def main():
|
||||
specific_time = "01945ca0-3189-76c0-9a8f-caf310fc8b8e"
|
||||
extension_path = "./target/debug/liblimbo_uuid.so"
|
||||
pipe = init_limbo()
|
||||
try:
|
||||
# before extension loads, assert no function
|
||||
run_test(pipe, "SELECT uuid4();", returns_null)
|
||||
run_test(pipe, "SELECT uuid4_str();", returns_null)
|
||||
run_test(pipe, f".load {extension_path}", returns_null)
|
||||
print("Extension loaded successfully.")
|
||||
run_test(pipe, "SELECT hex(uuid4());", validate_blob)
|
||||
run_test(pipe, "SELECT uuid4_str();", validate_string_uuid)
|
||||
run_test(pipe, "SELECT hex(uuid7());", validate_blob)
|
||||
run_test(
|
||||
pipe,
|
||||
"SELECT uuid7_timestamp_ms(uuid7()) / 1000;",
|
||||
)
|
||||
run_test(pipe, "SELECT uuid7_str();", validate_string_uuid)
|
||||
run_test(pipe, "SELECT uuid_str(uuid7());", validate_string_uuid)
|
||||
run_test(pipe, "SELECT hex(uuid_blob(uuid7_str()));", validate_blob)
|
||||
run_test(pipe, "SELECT uuid_str(uuid_blob(uuid7_str()));", validate_string_uuid)
|
||||
run_test(
|
||||
pipe,
|
||||
f"SELECT uuid7_timestamp_ms('{specific_time}') / 1000;",
|
||||
assert_specific_time,
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"Test FAILED: {e}")
|
||||
pipe.terminate()
|
||||
exit(1)
|
||||
pipe.terminate()
|
||||
print("All tests passed successfully.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,3 +1,18 @@
|
||||
#!/usr/bin/env tclsh
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/tester.tcl
|
||||
|
||||
do_execsql_test_on_specific_db {:memory:} basic-insert {
|
||||
create table temp (t1 integer, primary key (t1));
|
||||
insert into temp values (1);
|
||||
select * from temp;
|
||||
} {1}
|
||||
|
||||
do_execsql_test_on_specific_db {:memory:} must-be-int-insert {
|
||||
create table temp (t1 integer, primary key (t1));
|
||||
insert into temp values (1),(2.0),('3'),('4.0');
|
||||
select * from temp;
|
||||
} {1
|
||||
2
|
||||
3
|
||||
4}
|
||||
@@ -106,6 +106,14 @@ Jamie|coat
|
||||
Jamie|accessories
|
||||
Cindy|}
|
||||
|
||||
do_execsql_test left-join-row-id {
|
||||
select u.rowid, p.rowid from users u left join products as p on u.rowid = p.rowid where u.rowid >= 10 limit 5;
|
||||
} {10|10
|
||||
11|11
|
||||
12|
|
||||
13|
|
||||
14|}
|
||||
|
||||
do_execsql_test left-join-constant-condition-true {
|
||||
select u.first_name, p.name from users u left join products as p on true limit 1;
|
||||
} {Jamie|hat}
|
||||
|
||||
@@ -459,6 +459,118 @@ do_execsql_test bitwise-and-int-agg-int-agg {
|
||||
} {66}
|
||||
|
||||
|
||||
foreach {testname lhs rhs ans} {
|
||||
int-int 1 2 4
|
||||
int-neg_int 8 -2 2
|
||||
int-float 1 4.0 16
|
||||
int-text 1 'a' 1
|
||||
int-text_float 1 '3.0' 8
|
||||
int-text_int 1 '1' 2
|
||||
int-null 1 NULL {}
|
||||
int-int-overflow 1 64 0
|
||||
int-int-underflow 1 -64 0
|
||||
int-float-overflow 1 64.0 0
|
||||
int-float-underflow 1 -64.0 0
|
||||
} {
|
||||
do_execsql_test shift-left-$testname "SELECT $lhs << $rhs" $::ans
|
||||
}
|
||||
|
||||
foreach {testname lhs rhs ans} {
|
||||
float-int 1.0 2 4
|
||||
float-neg_int 8.0 -2 2
|
||||
float-float 1.0 4.0 16
|
||||
float-text 1.0 'a' 1
|
||||
float-text_float 1.0 '3.0' 8
|
||||
float-text_int 1.0 '1' 2
|
||||
float-null 1.0 NULL {}
|
||||
float-int-overflow 1.0 64 0
|
||||
float-int-underflow 1.0 -64 0
|
||||
float-float-overflow 1.0 64.0 0
|
||||
float-float-underflow 1.0 -64.0 0
|
||||
} {
|
||||
do_execsql_test shift-left-$testname "SELECT $lhs << $rhs" $::ans
|
||||
}
|
||||
|
||||
foreach {testname lhs rhs ans} {
|
||||
text-int 'a' 2 0
|
||||
text-float 'a' 4.0 0
|
||||
text-text 'a' 'a' 0
|
||||
text_int-text_int '1' '1' 2
|
||||
text_int-text_float '1' '3.0' 8
|
||||
text_int-text '1' 'a' 1
|
||||
text_float-text_int '1.0' '1' 2
|
||||
text_float-text_float '1.0' '3.0' 8
|
||||
text_float-text '1.0' 'a' 1
|
||||
text-null '1' NULL {}
|
||||
} {
|
||||
do_execsql_test shift-left-$testname "SELECT $lhs << $rhs" $::ans
|
||||
}
|
||||
|
||||
foreach {testname lhs rhs ans} {
|
||||
null-int NULL 2 {}
|
||||
null-float NULL 4.0 {}
|
||||
null-text NULL 'a' {}
|
||||
null-null NULL NULL {}
|
||||
} {
|
||||
do_execsql_test shift-left-$testname "SELECT $lhs << $rhs" $::ans
|
||||
}
|
||||
|
||||
foreach {testname lhs rhs ans} {
|
||||
int-int 8 2 2
|
||||
int-neg_int 8 -2 32
|
||||
int-float 8 1.0 4
|
||||
int-text 8 'a' 8
|
||||
int-text_float 8 '3.0' 1
|
||||
int-text_int 8 '1' 4
|
||||
int-null 8 NULL {}
|
||||
int-int-overflow 8 64 0
|
||||
int-int-underflow 8 -64 0
|
||||
int-float-overflow 8 64.0 0
|
||||
int-float-underflow 8 -64.0 0
|
||||
} {
|
||||
do_execsql_test shift-right-$testname "SELECT $lhs >> $rhs" $::ans
|
||||
}
|
||||
|
||||
foreach {testname lhs rhs ans} {
|
||||
float-int 8.0 2 2
|
||||
float-neg_int 8.0 -2 32
|
||||
float-float 8.0 1.0 4
|
||||
float-text 8.0 'a' 8
|
||||
float-text_float 8.0 '3.0' 1
|
||||
float-text_int 8.0 '1' 4
|
||||
float-null 8.0 NULL {}
|
||||
float-int-overflow 8.0 64 0
|
||||
float-int-underflow 8.0 -64 0
|
||||
float-float-overflow 8.0 64.0 0
|
||||
float-float-underflow 8.0 -64.0 0
|
||||
} {
|
||||
do_execsql_test shift-right-$testname "SELECT $lhs >> $rhs" $::ans
|
||||
}
|
||||
|
||||
foreach {testname lhs rhs ans} {
|
||||
text-int 'a' 2 0
|
||||
text-float 'a' 4.0 0
|
||||
text-text 'a' 'a' 0
|
||||
text_int-text_int '8' '1' 4
|
||||
text_int-text_float '8' '3.0' 1
|
||||
text_int-text '8' 'a' 8
|
||||
text_float-text_int '8.0' '1' 4
|
||||
text_float-text_float '8.0' '3.0' 1
|
||||
text_float-text '8.0' 'a' 8
|
||||
text-null '8' NULL {}
|
||||
} {
|
||||
do_execsql_test shift-right-$testname "SELECT $lhs >> $rhs" $::ans
|
||||
}
|
||||
|
||||
foreach {testname lhs rhs ans} {
|
||||
null-int NULL 2 {}
|
||||
null-float NULL 4.0 {}
|
||||
null-text NULL 'a' {}
|
||||
null-null NULL NULL {}
|
||||
} {
|
||||
do_execsql_test shift-right-$testname "SELECT $lhs >> $rhs" $::ans
|
||||
}
|
||||
|
||||
do_execsql_test bitwise-not-null {
|
||||
SELECT ~NULL
|
||||
} {}
|
||||
|
||||
@@ -80,6 +80,14 @@ do_execsql_test select_with_quoting_2 {
|
||||
select "users".`id` from users where `users`.[id] = 5;
|
||||
} {5}
|
||||
|
||||
do_execsql_test select-rowid {
|
||||
select rowid, first_name from users u where rowid = 5;
|
||||
} {5|Edward}
|
||||
|
||||
do_execsql_test select-rowid-2 {
|
||||
select u.rowid, first_name from users u where rowid = 5;
|
||||
} {5|Edward}
|
||||
|
||||
do_execsql_test seekrowid {
|
||||
select * from users u where u.id = 5;
|
||||
} {"5|Edward|Miller|christiankramer@example.com|725-281-1033|08522 English Plain|Lake Keith|ID|23283|15"}
|
||||
|
||||
@@ -365,3 +365,21 @@ do_execsql_test nested-parens-conditionals-and-double-or {
|
||||
8171|Andrea|Lee|dgarrison@example.com|001-594-430-0646|452 Anthony Stravenue|Sandraville|CA|28572|12
|
||||
9110|Anthony|Barrett|steven05@example.net|(562)928-9177x8454|86166 Foster Inlet Apt. 284|North Jeffreyburgh|CA|80147|97
|
||||
9279|Annette|Lynn|joanne37@example.com|(272)700-7181|2676 Laura Points Apt. 683|Tristanville|NY|48646|91}}
|
||||
|
||||
# Regression test for nested parens + OR + AND. This returned 0 rows before the fix.
|
||||
# It should always return 1 row because it is true for id = 6.
|
||||
do_execsql_test nested-parens-and-inside-or-regression-test {
|
||||
SELECT count(1) FROM users
|
||||
WHERE (
|
||||
(
|
||||
(
|
||||
(id != 5)
|
||||
AND
|
||||
(id = 5 OR TRUE)
|
||||
)
|
||||
OR FALSE
|
||||
)
|
||||
AND
|
||||
(id = 6 OR FALSE)
|
||||
);
|
||||
} {1}
|
||||
Reference in New Issue
Block a user