From 5060bb6e8c86656d8476308350e75568395793ee Mon Sep 17 00:00:00 2001 From: raftedproc <71657594+raftedproc@users.noreply.github.com> Date: Fri, 17 Feb 2023 20:38:01 +0300 Subject: [PATCH] fix: extra tests and sqlite module bump to 0.17.1 [fixes VM-226] (#7) --- build.sh | 2 +- src/test.rs | 551 ++++++++++++++++++++++++++++++++++++++++++++++++- tests/tests.rs | 40 ++++ 3 files changed, 585 insertions(+), 8 deletions(-) diff --git a/build.sh b/build.sh index ad0d3e6..a27272b 100755 --- a/build.sh +++ b/build.sh @@ -13,4 +13,4 @@ mkdir -p artifacts cp target/wasm32-wasi/release/test.wasm artifacts/ # download SQLite 3 to use in tests -curl -L https://github.com/fluencelabs/sqlite/releases/download/v0.15.0_w/sqlite3.wasm -o artifacts/sqlite3.wasm +curl -L https://github.com/fluencelabs/sqlite/releases/download/v0.17.1_w/sqlite3.wasm -o artifacts/sqlite3.wasm diff --git a/src/test.rs b/src/test.rs index e3a0ca4..812545f 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,9 +1,9 @@ use marine_rs_sdk::marine; -use marine_sqlite_connector::State; -use marine_sqlite_connector::Value; +use marine_sqlite_connector::{version, State, Value}; pub fn main() {} +// Projection with binding in a filter #[marine] pub fn test1() { let connection = marine_sqlite_connector::open(":memory:").unwrap(); @@ -11,9 +11,9 @@ pub fn test1() { connection .execute( " - CREATE TABLE users (name TEXT, age INTEGER); - INSERT INTO users VALUES ('Alice', 42); - INSERT INTO users VALUES ('Bob', 69); + CREATE TABLE users (name TEXT, age INTEGER, weight DOUBLE); + INSERT INTO users VALUES ('Alice', 42, 100.15); + INSERT INTO users VALUES ('Bob', 69, 200.11); ", ) .unwrap(); @@ -27,8 +27,39 @@ pub fn test1() { assert_eq!(statement.next().unwrap(), State::Row); assert_eq!(statement.read::(0).unwrap(), "Bob"); assert_eq!(statement.read::(1).unwrap(), 69); + assert_eq!(statement.read::(2).unwrap(), 200.11); + assert_eq!(connection.total_changes(), 2); + + let mut statement = connection + .prepare("SELECT * FROM users WHERE weight > ?") + .unwrap(); + + statement.bind(1, 150.1).unwrap(); + + assert_eq!(statement.next().unwrap(), State::Row); + assert_eq!(statement.read::(0).unwrap(), "Bob"); + assert_eq!(statement.read::(1).unwrap(), 69); + assert_eq!(statement.read::(2).unwrap(), 200.11); + assert_eq!(connection.total_changes(), 2); + + connection + .execute("UPDATE users SET age = 20 WHERE age = 69;") + .unwrap(); + let mut cursor = connection + .prepare("SELECT * FROM users WHERE age < ?") + .unwrap() + .cursor(); + cursor.bind(&[Value::Integer(30)]).unwrap(); + + while let Some(row) = cursor.next().unwrap() { + assert_eq!(row[0].as_string().unwrap(), "Bob"); + assert_eq!(row[1].as_integer().unwrap(), 20); + assert_eq!(row[2].as_float().unwrap(), 200.11); + } + assert_eq!(connection.total_changes(), 3); } +// Another projection with binding in a filter #[marine] pub fn test2() { let connection = marine_sqlite_connector::open(":memory:").unwrap(); @@ -54,8 +85,10 @@ pub fn test2() { assert_eq!(row[0].as_string().unwrap(), "Bob"); assert_eq!(row[1].as_integer().unwrap(), 69); } + assert_eq!(connection.total_changes(), 2); } +// BLOB binding #[marine] pub fn test3() { let connection = marine_sqlite_connector::open(":memory:").unwrap(); @@ -63,13 +96,13 @@ pub fn test3() { connection .execute( " - CREATE TABLE test (number INTEGER, blob BLOB NOT NULL); + CREATE TABLE test (number INTEGER, blob BLOB NOT NULL, blob_nullable BLOB); ", ) .unwrap(); let mut cursor = connection - .prepare("INSERT OR REPLACE INTO test VALUES (?, ?)") + .prepare("INSERT OR REPLACE INTO test (number, blob) VALUES (?, ?);") .unwrap(); cursor.bind(1, &Value::Integer(50)).unwrap(); @@ -77,8 +110,10 @@ pub fn test3() { // check that blob is not null assert!(cursor.next().is_ok()); + assert_eq!(connection.total_changes(), 1); } +// BLOB #[marine] pub fn test4() { let connection = marine_sqlite_connector::open(":memory:").unwrap(); @@ -110,4 +145,506 @@ pub fn test4() { while let Some(row) = cursor.next().unwrap() { assert_eq!(row[0].as_binary().unwrap().to_vec(), vec![1, 2, 3]); } + assert_eq!(connection.total_changes(), 1); +} + +//GROUP BY + ORDER BY +#[marine] +pub fn test5() { + let connection = marine_sqlite_connector::open(":memory:").unwrap(); + + connection + .execute( + " + CREATE TABLE users (name TEXT, age INTEGER); + INSERT INTO users VALUES ('Alice', 42); + INSERT INTO users VALUES ('2ndAlice', 42); + ", + ) + .unwrap(); + + connection + .execute( + " + INSERT INTO users VALUES ('PreAlice', 41); + INSERT INTO users VALUES ('AliceInAthens', 43); + INSERT INTO users VALUES ('Bob', 69); + ", + ) + .unwrap(); + + let mut cursor = connection + .prepare("SELECT age, count(*) FROM users GROUP BY age ORDER BY 1") + .unwrap() + .cursor(); + + let mut res: Vec = vec![]; + while let Some(row) = cursor.next().unwrap() { + res.push(row[0].as_integer().unwrap()); + } + assert_eq!(res, vec![41, 42, 43, 69]); + // Check the second column + assert_eq!(connection.total_changes(), 5); + connection.execute("DROP TABLE users;").unwrap(); + assert_eq!(connection.execute("DROP TABLE users;").is_err(), true); +} + +// ORDER BY +#[marine] +pub fn test6() { + let connection = marine_sqlite_connector::open(":memory:").unwrap(); + + connection + .execute( + " + CREATE TABLE users (name TEXT, age INTEGER); + INSERT INTO users VALUES ('Alice', 42); + INSERT INTO users VALUES ('2ndAlice', 42); + INSERT INTO users VALUES ('PreAlice', 41); + INSERT INTO users VALUES ('AliceInAthens', 43); + INSERT INTO users VALUES ('Bob', 69); + ", + ) + .unwrap(); + + let mut statement = connection + .prepare("SELECT * FROM users WHERE age >= ? ORDER BY age ASC") + .unwrap(); + + statement.bind(1, 43).unwrap(); + + assert_eq!(statement.next().unwrap(), State::Row); + assert_eq!(statement.read::(0).unwrap(), "AliceInAthens"); + assert_eq!(statement.read::(1).unwrap(), 43); + + assert_eq!(statement.next().unwrap(), State::Row); + assert_eq!(statement.read::(0).unwrap(), "Bob"); + assert_eq!(statement.read::(1).unwrap(), 69); + assert_eq!(connection.total_changes(), 5); +} + +// view +#[marine] +pub fn test7() { + let connection = marine_sqlite_connector::open(":memory:").unwrap(); + + connection + .execute( + " + CREATE TABLE users (name TEXT, age INTEGER); + INSERT INTO users VALUES ('Alice', 42); + INSERT INTO users VALUES ('2ndAlice', 42); + INSERT INTO users VALUES ('PreAlice', 41); + INSERT INTO users VALUES ('AliceInAthens', 43); + INSERT INTO users VALUES ('Bob', 69); + CREATE VIEW view1 as SELECT * FROM users WHERE age >= 43; + ", + ) + .unwrap(); + + let mut cursor = connection + .prepare("SELECT name, count(*) FROM view1 GROUP BY name ORDER BY name") + .unwrap() + .cursor(); + let golden = vec!["AliceInAthens", "Bob"]; + let mut golden_it = golden.iter(); + while let Some(row) = cursor.next().unwrap() { + assert_eq!(row[0].as_string().unwrap(), *golden_it.next().unwrap()); + } + assert_eq!(connection.total_changes(), 5); +} + +#[marine] +pub fn test8() { + assert!(version() >= 3040001 as usize); +} + +// json +#[marine] +pub fn test9() { + let connection = marine_sqlite_connector::open(":memory:").unwrap(); + + connection + .execute( + "CREATE TABLE j2(id INTEGER PRIMARY KEY, json, src); + INSERT INTO j2(id,json,src) + VALUES(1,'{ + \"firstName\": \"John\", + \"lastName\": \"Smith\", + \"isAlive\": true, + \"age\": 25, + \"address\": { + \"streetAddress\": \"21 2nd Street\", + \"city\": \"New York\", + \"state\": \"NY\", + \"postalCode\": \"10021-3100\" + }, + \"phoneNumbers\": [ + { + \"type\": \"home\", + \"number\": \"212 555-1234\" + }, + { + \"type\": \"office\", + \"number\": \"646 555-4567\" + } + ], + \"children\": [], + \"spouse\": null + }','https://en.wikipedia.org/wiki/JSON'); + INSERT INTO j2(id,json,src) + VALUES(2, '{ + \"id\": \"0001\", + \"type\": \"donut\", + \"name\": \"Cake\", + \"ppu\": 0.55, + \"batters\": + { + \"batter\": + [ + { \"id\": \"1001\", \"type\": \"Regular\" }, + { \"id\": \"1002\", \"type\": \"Chocolate\" }, + { \"id\": \"1003\", \"type\": \"Blueberry\" }, + { \"id\": \"1004\", \"type\": \"Devil''s Food\" } + ] + }, + \"topping\": + [ + { \"id\": \"5001\", \"type\": \"None\" }, + { \"id\": \"5002\", \"type\": \"Glazed\" }, + { \"id\": \"5005\", \"type\": \"Sugar\" }, + { \"id\": \"5007\", \"type\": \"Powdered Sugar\" }, + { \"id\": \"5006\", \"type\": \"Chocolate with Sprinkles\" }, + { \"id\": \"5003\", \"type\": \"Chocolate\" }, + { \"id\": \"5004\", \"type\": \"Maple\" } + ] + }','https://adobe.github.io/Spry/samples/data_region/JSONDataSetSample.html'); + INSERT INTO j2(id,json,src) + VALUES(3,'[ + { + \"id\": \"0001\", + \"type\": \"donut\", + \"name\": \"Cake\", + \"ppu\": 0.55, + \"batters\": + { + \"batter\": + [ + { \"id\": \"1001\", \"type\": \"Regular\" }, + { \"id\": \"1002\", \"type\": \"Chocolate\" }, + { \"id\": \"1003\", \"type\": \"Blueberry\" }, + { \"id\": \"1004\", \"type\": \"Devil''s Food\" } + ] + }, + \"topping\": + [ + { \"id\": \"5001\", \"type\": \"None\" }, + { \"id\": \"5002\", \"type\": \"Glazed\" }, + { \"id\": \"5005\", \"type\": \"Sugar\" }, + { \"id\": \"5007\", \"type\": \"Powdered Sugar\" }, + { \"id\": \"5006\", \"type\": \"Chocolate with Sprinkles\" }, + { \"id\": \"5003\", \"type\": \"Chocolate\" }, + { \"id\": \"5004\", \"type\": \"Maple\" } + ] + }, + { + \"id\": \"0002\", + \"type\": \"donut\", + \"name\": \"Raised\", + \"ppu\": 0.55, + \"batters\": + { + \"batter\": + [ + { \"id\": \"1001\", \"type\": \"Regular\" } + ] + }, + \"topping\": + [ + { \"id\": \"5001\", \"type\": \"None\" }, + { \"id\": \"5002\", \"type\": \"Glazed\" }, + { \"id\": \"5005\", \"type\": \"Sugar\" }, + { \"id\": \"5003\", \"type\": \"Chocolate\" }, + { \"id\": \"5004\", \"type\": \"Maple\" } + ] + }, + { + \"id\": \"0003\", + \"type\": \"donut\", + \"name\": \"Old Fashioned\", + \"ppu\": 0.55, + \"batters\": + { + \"batter\": + [ + { \"id\": \"1001\", \"type\": \"Regular\" }, + { \"id\": \"1002\", \"type\": \"Chocolate\" } + ] + }, + \"topping\": + [ + { \"id\": \"5001\", \"type\": \"None\" }, + { \"id\": \"5002\", \"type\": \"Glazed\" }, + { \"id\": \"5003\", \"type\": \"Chocolate\" }, + { \"id\": \"5004\", \"type\": \"Maple\" } + ] + } + ]','https://adobe.github.io/Spry/samples/data_region/JSONDataSetSample.html');", + ) + .unwrap(); + let mut cursor = connection + .prepare("SELECT id, json_valid(json), json_type(json), '|' FROM j2 ORDER BY id;") + .unwrap() + .cursor(); + let golden = vec!["object", "object", "array"]; + let mut golden_it = golden.iter(); + while let Some(row) = cursor.next().unwrap() { + assert_eq!(row[2].as_string().unwrap(), *golden_it.next().unwrap()); + } + + assert_eq!(connection.total_changes(), 3); +} + +// fts5 +#[marine] +pub fn test10() { + let connection = marine_sqlite_connector::open(":memory:").unwrap(); + + connection + .execute( + " + CREATE VIRTUAL TABLE email USING fts5(sender, subject, body); + INSERT INTO email VALUES ('Alice', 'How deep is the hole', 'Nobody knows'); + INSERT INTO email VALUES ('Wonderland', 'Hatter', 'Normality. I know this'); + INSERT INTO email VALUES ('Rabbit the optimist', 'He never sleeps', 'Never I said you know'); + ", + ) + .unwrap(); + + let mut cursor = connection + .prepare("SELECT sender FROM email WHERE body MATCH 'know';") + .unwrap() + .cursor(); + let golden = vec!["Wonderland", "Rabbit the optimist"]; + let mut golden_it = golden.iter(); + while let Some(row) = cursor.next().unwrap() { + assert_eq!(row[0].as_string().unwrap(), *golden_it.next().unwrap()); + } +} + +// rtree +#[marine] +pub fn test11() { + let connection = marine_sqlite_connector::open(":memory:").unwrap(); + + connection + .execute( + " + CREATE VIRTUAL TABLE demo_index USING rtree( + id, -- Integer primary key + minX, maxX, -- Minimum and maximum X coordinate + minY, maxY -- Minimum and maximum Y coordinate + ); + INSERT INTO demo_index VALUES + (28215, -80.781227, -80.604706, 35.208813, 35.297367), + (28216, -80.957283, -80.840599, 35.235920, 35.367825), + (28217, -80.960869, -80.869431, 35.133682, 35.208233), + (28226, -80.878983, -80.778275, 35.060287, 35.154446), + (28227, -80.745544, -80.555382, 35.130215, 35.236916), + (28244, -80.844208, -80.841988, 35.223728, 35.225471), + (28262, -80.809074, -80.682938, 35.276207, 35.377747), + (28269, -80.851471, -80.735718, 35.272560, 35.407925), + (28270, -80.794983, -80.728966, 35.059872, 35.161823), + (28273, -80.994766, -80.875259, 35.074734, 35.172836), + (28277, -80.876793, -80.767586, 35.001709, 35.101063), + (28278, -81.058029, -80.956375, 35.044701, 35.223812), + (28280, -80.844208, -80.841972, 35.225468, 35.227203), + (28282, -80.846382, -80.844193, 35.223972, 35.225655); + ", + ) + .unwrap(); + + let mut cursor = connection + .prepare( + "SELECT A.id FROM demo_index AS A, demo_index AS B + WHERE A.maxX>=B.minX AND A.minX<=B.maxX + AND A.maxY>=B.minY AND A.minY<=B.maxY + AND B.id=28269 ORDER BY 1;", + ) + .unwrap() + .cursor(); + let golden = vec![28215, 28216, 28262, 28269]; + let mut golden_it = golden.iter(); + while let Some(row) = cursor.next().unwrap() { + assert_eq!(row[0].as_integer().unwrap(), *golden_it.next().unwrap()); + } +} + +// triggers +#[marine] +pub fn test12() { + let connection = marine_sqlite_connector::open(":memory:").unwrap(); + + connection + .execute( + " + CREATE TABLE leads ( + id integer PRIMARY KEY, + first_name text NOT NULL, + last_name text NOT NULL, + phone text NOT NULL, + email text NOT NULL + ); + ", + ) + .unwrap(); + + connection + .execute( + " + CREATE TRIGGER validate_email_before_insert_leads + BEFORE INSERT ON leads + BEGIN + SELECT + CASE + WHEN NEW.email NOT LIKE '%_@__%.__%' THEN + RAISE (ABORT,'Invalid email address') + END; + END; + ", + ) + .unwrap(); + + connection + .execute( + " + INSERT INTO leads (first_name, last_name, email, phone) + VALUES ('John', 'Doe', 'john.doe@sqlitetutorial.net', '4089009334'); + ", + ) + .unwrap(); + + assert!(connection + .execute( + " + INSERT INTO leads (first_name,last_name,email,phone) + VALUES('John','Doe','jjj','4089009334'); + ", + ) + .is_err()); + + let mut cursor = connection + .prepare( + " + SELECT first_name, last_name, email FROM leads; + ", + ) + .unwrap() + .cursor(); + while let Some(row) = cursor.next().unwrap() { + assert_eq!(row[0].as_string().unwrap(), "John"); + assert_eq!(row[2].as_string().unwrap(), "john.doe@sqlitetutorial.net"); + } + // ON UPDATE + connection + .execute( + " + CREATE TABLE lead_logs ( + id INTEGER PRIMARY KEY, + old_id int, + new_id int, + old_phone text, + new_phone text, + old_email text, + new_email text, + user_action text, + created_at text + ); + ", + ) + .unwrap(); + + connection + .execute( + " + CREATE TRIGGER log_contact_after_update + AFTER UPDATE ON leads + WHEN old.phone <> new.phone + OR old.email <> new.email + BEGIN + INSERT INTO lead_logs ( + old_id, + new_id, + old_phone, + new_phone, + old_email, + new_email, + user_action, + created_at + ) + VALUES + ( + old.id, + new.id, + old.phone, + new.phone, + old.email, + new.email, + 'UPDATE', + DATETIME('NOW') + ) ; + END; + ", + ) + .unwrap(); + + connection + .execute( + " + UPDATE leads + SET + last_name = 'Smith' + WHERE + id = 1; + ", + ) + .unwrap(); + let mut statement = connection + .prepare( + " + SELECT count(*) FROM lead_logs; + ", + ) + .unwrap(); + + assert_eq!(statement.next().unwrap(), State::Row); + assert_eq!(statement.read::(0).unwrap(), 0); + connection + .execute( + " + UPDATE leads + SET + phone = '4089998888', + email = 'john.smith@sqlitetutorial.net' + WHERE + id = 1; + ", + ) + .unwrap(); + let mut cursor = connection + .prepare( + " + SELECT old_phone,new_phone,old_email,new_email,user_action + FROM lead_logs; + ", + ) + .unwrap() + .cursor(); + while let Some(row) = cursor.next().unwrap() { + assert_eq!(row[0].as_string().unwrap(), "4089009334"); + assert_eq!(row[1].as_string().unwrap(), "4089998888"); + assert_eq!(row[2].as_string().unwrap(), "john.doe@sqlitetutorial.net"); + assert_eq!(row[3].as_string().unwrap(), "john.smith@sqlitetutorial.net"); + assert_eq!(row[4].as_string().unwrap(), "UPDATE"); + } } diff --git a/tests/tests.rs b/tests/tests.rs index ff0ce7b..1030d6a 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -36,4 +36,44 @@ mod tests { fn test4(test: marine_test_env::test::ModuleInterface) { test.test4() } + + #[marine_test(config_path = "../Config.toml", modules_dir = "../artifacts/")] + fn test5(test: marine_test_env::test::ModuleInterface) { + test.test5() + } + + #[marine_test(config_path = "../Config.toml", modules_dir = "../artifacts/")] + fn test6(test: marine_test_env::test::ModuleInterface) { + test.test6() + } + + #[marine_test(config_path = "../Config.toml", modules_dir = "../artifacts/")] + fn test7(test: marine_test_env::test::ModuleInterface) { + test.test7() + } + + #[marine_test(config_path = "../Config.toml", modules_dir = "../artifacts/")] + fn test8(test: marine_test_env::test::ModuleInterface) { + test.test8() + } + + #[marine_test(config_path = "../Config.toml", modules_dir = "../artifacts/")] + fn test9(test: marine_test_env::test::ModuleInterface) { + test.test9() + } + + #[marine_test(config_path = "../Config.toml", modules_dir = "../artifacts/")] + fn test10(test: marine_test_env::test::ModuleInterface) { + test.test10() + } + + #[marine_test(config_path = "../Config.toml", modules_dir = "../artifacts/")] + fn test11(test: marine_test_env::test::ModuleInterface) { + test.test11() + } + + #[marine_test(config_path = "../Config.toml", modules_dir = "../artifacts/")] + fn test12(test: marine_test_env::test::ModuleInterface) { + test.test12() + } }