Есть простая программа — HTTP-сервис.
При обработке get-запроса программа использует файловую базу данных sqlite. Для работы с БД используется rocket_db_pools.
Все сделано и работает как описано вот в этой статье: https://docs.rs/rocket_db_pools/latest/rocket_db_pools/
В обычном "рабочем" режиме библиотека rocket "автоматически" вызывает функцию-обработчик get-запроса, и в качестве параметра передает ей объект соединения с БД Connection<LogsDB>. Этот параметр-объект создается "автоматически" внутри библиотеки rocket.
Проблема возникла при реализации тестовой функции.
Я из тестовой функции хочу вызвать функцию, которая реализует обработку get-запроса. Для этого нужно "вручную" создать объект Connection<LogsDB> и передать его в обработчик-запроса.
Но как это сделать?.
Как в тестовой функции "вручную" создать объект Connection<LogsDB>?
Я читал описание этой библиотеки, но так и не смог ничего найти.
Подскажите пожалуйста.
Вот код программы:
use rocket::get;
use rocket_db_pools::{Connection, Database};
use rocket_db_pools::sqlx::Row;
#[derive(Database)]
#[database("data_store")]
struct LogsDB(sqlx::SqlitePool);
#[get("/<id>")]
//async fn read(mut db: Connection<LogsDB>, id: i64) -> Option<Log> {
async fn read(mut db: Connection<LogsDB>, id: i64) {
let res = sqlx::query("SELECT content FROM logs WHERE id = ?").bind(id)
.fetch_one(&mut **db).await;
// .and_then(|r| Ok(log::Log(r.try_get(0)?)))
// .ok()
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_load_requests_and_write_updxmlfile() {
let l_rocket = rocket::build().attach(LogsDB::init());
//let l_database = MarkingDatabase::init();let l_db1 = LogsDB::fetch(&l_rocket).unwrap();
let mut l_db_connection1 = l_db1.aquire().await.unwrap();
read(l_db_connection1, 50)?;
}
}
Я так понимаю, что объект Connection<LogsDB> надо как-то получить в строке
"let mut l_db_connection1 = l_db1.aquire().await.unwrap();"
Компилятор выдает ошибку на эту строку.
Re: Создать соединение с БД из тестовой функции (rocket_db_pools)
Здравствуйте, zelenprog, Вы писали:
Z>Подскажите пожалуйста.
Если вот ни разу не про rust, но:
А счего бы вдруг sql запросы отправляются в connection напрямую из get обработчика?
Разве не должна быть модель, реализующая интрефейс (или трейт), и её мы подменяем в тестах хоть на inmemory, хоть на другое хранилище. Мы же не хранилище тестируем, так?
Спасибо за внимание
Re[2]: Создать соединение с БД из тестовой функции (rocket_db_pools)
Проверил...
Выражение "l_db1.0.acquire().await.unwrap();" возвращает значение типа "sqlx::pool::PoolConnection<sqlx::Sqlite>".
При вызове функции "read" компилятор выдает ошибку:
error[E0308]: mismatched types
--> src\lib.rs:32:14
|
32 | read(l_db_connection1, 50);
| ---- ^^^^^^^^^^^^^^^^ expected `Connection<LogsDB>`, found `PoolConnection<Sqlite>`
| |
| arguments to this function are incorrect
|
= note: expected struct `rocket_db_pools::Connection<LogsDB>`
found struct `PoolConnection<Sqlite>`
Значит, надо еще какие-то манипуляции сделать, чтобы добраться до типа Connection<LogsDB>.
Получается, мы получили пул соединений.
Может быть теперь надо из этого пула взять одно из соединений? Или создать новое?
Z>>
Z>>struct LogsDB(sqlx::SqlitePool);
Z>>
SP>это кортеж...
Насколько я помню из документации, при использовании кортежа из одного элемента в конце надо обязательно ставить запятую.
Чтобы отличить кортеж с одним элементом от простого выражения в скобках.
Или я что-то неправильно понял?
Re[2]: Создать соединение с БД из тестовой функции (rocket_d
D>А счего бы вдруг sql запросы отправляются в connection напрямую из get обработчика?
Вообще в "рабочем" коде из get-обработчика вызывается специальная функция-реализация, которая вызывает другие объекты и методы и т.д.
Цепочка вызовов получается достаточно длинная.
Это я для упрощения примера сделал показательный get-обработчик, чтобы показать в чем у меня проблема.
А проблема у меня — получить соединение с БД конкретного типа, который нужен для чтения данных из БД.
D>Разве не должна быть модель, реализующая интрефейс (или трейт), и её мы подменяем в тестах хоть на inmemory, хоть на другое хранилище...
Вообще в принципе — да, согласен.
Но хранилище не надо подменять.
D>... Мы же не хранилище тестируем, так?
В данном случае мне надо протестировать полную реализацию get-запроса, которая выполняет много разных действий и в том числе работает с хранилищем.
При работе с хранилищем (например при чтении из хранилища данных) используется вот такой код:
async fn read(mut db: Connection<LogsDB>, id: i64) {
let res = sqlx::query("SELECT content FROM logs WHERE id = ?").bind(id)
.fetch_one(&mut **db).await;
....
db — это параметр типа "Connection<LogsDB>", который передается в эту функцию.
Вот хорошо бы этот код тоже протестировать.
Конечно, можно было бы вместо параметра "db" типа "Connection<LogsDB>" сделать интерфейс (трейт) с одной функцией "get_connection()" и передавать в функцию этот трейт вместо "Connection<LogsDB>".
Но тогда я не протестирую "рабочий" код.
Здравствуйте, zelenprog, Вы писали:
Z>Значит, надо еще какие-то манипуляции сделать, чтобы добраться до типа Connection<LogsDB>.
надо. Почитайте документацию. Там уже у Pool есть метод acquire, который как раз возвращает Connection. Возможно подойдёт.
Z>Насколько я помню из документации, при использовании кортежа из одного элемента в конце надо обязательно ставить запятую.
при объявлении структуры не надо. Это при паттерн-матчинге и при объявлении кортежа на лету только
let tuple = (42u8, );
let (int, ) = tuple;
Re[4]: Создать соединение с БД из тестовой функции (rocket_db_pools)
SP>Почитайте документацию. Там уже у Pool есть метод acquire, который как раз возвращает Connection. Возможно подойдёт.
В том то и дело, что читаю уже почти неделю.
А найти не могу. Маловато опыта в Rust-е.
Сегодня мне "помог" DeepSeek.
Как-то я догадался указать в запросе версию библиотеки rocket_db_pools 0.1.0.
И как только я указал эту версию он мне выдал ответ, который все объясняет:
В версии rocket_db_pools 0.1.0 API немного отличается от более поздних версий. В этой версии Connection<MyDB> не предоставляет прямого способа создания вручную, как в более новых версиях.
Re[3]: Создать соединение с БД из тестовой функции (rocket_d
Здравствуйте, zelenprog, Вы писали:
Z>В данном случае мне надо протестировать полную реализацию get-запроса, которая выполняет много разных действий и в том числе работает с хранилищем. Z>При работе с хранилищем (например при чтении из хранилища данных) используется вот такой код: Z>
Z>async fn read(mut db: Connection<LogsDB>, id: i64) {
Z> let res = sqlx::query("SELECT content FROM logs WHERE id = ?").bind(id)
Z> .fetch_one(&mut **db).await;
Z> ....
Z>
Z>db — это параметр типа "Connection<LogsDB>", который передается в эту функцию. Z>Вот хорошо бы этот код тоже протестировать.
Z>Конечно, можно было бы вместо параметра "db" типа "Connection<LogsDB>" сделать интерфейс (трейт) с одной функцией "get_connection()" и передавать в функцию этот трейт вместо "Connection<LogsDB>". Z>Но тогда я не протестирую "рабочий" код.
Я точно не имею огромного опыта в rust, и не знаю нюансов использования какого там фреймворка, но что значит "рабочий код"? Точно не внутри библиотечный — же. Мы не проверяем как именно работает условный fetch, нам надо убедиться, что "рабочий код" создал корректный запрос и адекватно реагирует на описанные в документации этого fetch ошибки.
Тогда мы мокаем эту самую библиотечную сущность и описывем в них тестовое поведение для "рабочего кода". Используя старые добрые разные реализации интерфейсов, которые и дёргаем в нашем "рабочем коде".