94 lines
4.4 KiB
Plaintext
94 lines
4.4 KiB
Plaintext
#include "testlib.h"
|
|
|
|
RENDER(Request& context)
|
|
{
|
|
u64 passed = 0;
|
|
u64 failed = 0;
|
|
u64 skipped = 0;
|
|
|
|
auto check = [&](String name, bool ok, String detail)
|
|
{
|
|
site_tests_case(name, ok ? "pass" : "fail", detail);
|
|
if(ok)
|
|
passed++;
|
|
else
|
|
failed++;
|
|
};
|
|
|
|
site_tests_page_start("SQLite", "SQLite connector coverage for prepared params, result typing, metadata, and cleanup.");
|
|
|
|
String db_path = "/tmp/uce-site-tests-sqlite.sqlite";
|
|
file_unlink(db_path);
|
|
file_unlink(db_path + "-wal");
|
|
file_unlink(db_path + "-shm");
|
|
|
|
SQLite* db = sqlite_connect(db_path);
|
|
check("sqlite_connect()", db && db->connection != 0 && sqlite_error(db) == "connected", sqlite_error(db));
|
|
|
|
sqlite_query(db, "create table users(id integer primary key autoincrement, email text not null unique, visits integer not null, rating real not null)");
|
|
check("sqlite create table", sqlite_error(db) == "ok", sqlite_error(db));
|
|
|
|
sqlite_query(db, "create table dropped(id integer); insert into dropped values(1);");
|
|
String multi_statement_error = sqlite_error(db);
|
|
StringMap dropped_params;
|
|
dropped_params["name"] = "dropped";
|
|
DValue dropped_tables = sqlite_query(db, "select name from sqlite_master where type = 'table' and name = :name", dropped_params);
|
|
check("sqlite_query() rejects multi-statement SQL", multi_statement_error.find("exactly one SQL statement") != String::npos && dropped_tables["0"]["name"].to_string() == "", multi_statement_error + " tables=" + json_encode(dropped_tables));
|
|
|
|
StringMap ada;
|
|
ada["email"] = "ada@example.test";
|
|
ada["visits"] = "7";
|
|
ada["rating"] = "4.5";
|
|
sqlite_query(db, "insert into users(email, visits, rating) values(:email, :visits, :rating)", ada);
|
|
u64 ada_id = sqlite_insert_id(db);
|
|
check("sqlite named insert", ada_id == 1 && sqlite_affected_rows(db) == 1, "id=" + std::to_string(ada_id) + " affected=" + std::to_string((u64)sqlite_affected_rows(db)) + " error=" + sqlite_error(db));
|
|
|
|
StringMap bob;
|
|
bob["email"] = "bob@example.test";
|
|
bob["visits"] = "3";
|
|
bob["rating"] = "2.25";
|
|
sqlite_query(db, "insert into users(email, visits, rating) values(:email, :visits, :rating)", bob);
|
|
check("sqlite second insert", sqlite_insert_id(db) == 2 && sqlite_affected_rows(db) == 1, "id=" + std::to_string(sqlite_insert_id(db)));
|
|
|
|
StringMap query_params;
|
|
query_params["min_visits"] = "4";
|
|
DValue rows = sqlite_query(db, "select id, email, visits, rating from users where visits >= :min_visits order by id", query_params);
|
|
check("sqlite_query() rows", rows.is_array() && rows["0"]["email"].to_string() == "ada@example.test" && rows["0"]["visits"].to_s64() == 7 && rows["0"]["rating"].to_f64() > 4.49, json_encode(rows));
|
|
|
|
sqlite_query(db, "select id from users where id = ?", query_params);
|
|
String positional_error = sqlite_error(db);
|
|
check("sqlite_query() rejects positional placeholders", positional_error.find("positional ? placeholders are not supported") != String::npos, positional_error);
|
|
|
|
sqlite_query(db, "select id from users where id = @id", query_params);
|
|
String non_colon_error = sqlite_error(db);
|
|
check("sqlite_query() rejects non-colon placeholders", non_colon_error.find("only supports :name placeholders") != String::npos, non_colon_error);
|
|
|
|
sqlite_query(db, "update users set visits = visits + 1 where email = :email", ada);
|
|
check("sqlite_affected_rows()", sqlite_affected_rows(db) == 1, std::to_string((u64)sqlite_affected_rows(db)));
|
|
|
|
sqlite_query(db, "select nope from missing_table");
|
|
check("sqlite_error()", sqlite_error(db).find("sqlite prepare failed") != String::npos && sqlite_error(db).find("no such table") != String::npos, sqlite_error(db));
|
|
|
|
sqlite_disconnect(db);
|
|
|
|
String cleanup_db_path = db_path + ".cleanup";
|
|
file_unlink(cleanup_db_path);
|
|
file_unlink(cleanup_db_path + "-wal");
|
|
file_unlink(cleanup_db_path + "-shm");
|
|
SQLite* cleanup_db = sqlite_connect(cleanup_db_path);
|
|
sqlite_query(cleanup_db, "create table cleanup_probe(id integer)");
|
|
cleanup_sqlite_connections();
|
|
check("sqlite request cleanup releases tracked wrappers", context.resources.sqlite_connections.size() == 0, "tracked=" + std::to_string((u64)context.resources.sqlite_connections.size()));
|
|
sqlite_disconnect(cleanup_db);
|
|
file_unlink(cleanup_db_path);
|
|
file_unlink(cleanup_db_path + "-wal");
|
|
file_unlink(cleanup_db_path + "-shm");
|
|
|
|
file_unlink(db_path);
|
|
file_unlink(db_path + "-wal");
|
|
file_unlink(db_path + "-shm");
|
|
|
|
site_tests_summary(passed, failed, skipped, "SQLite tests write only under /tmp/uce-site-tests-sqlite.sqlite and clean up WAL/SHM sidecars.");
|
|
site_tests_page_end();
|
|
}
|