Lines
77.66 %
Functions
10.34 %
Branches
100 %
pub mod user {
use crate::db::DBError;
use crate::error::ServerError;
use crate::user::User;
use finance::tag::Tag;
use sqlx::types::Uuid;
pub struct ScriptInfo {
pub id: Uuid,
pub name: Option<String>,
pub size: i32,
pub enabled: Option<String>,
}
pub struct ScriptDetail {
pub bytecode: Vec<u8>,
impl User {
pub async fn list_scripts(&self) -> Result<Vec<ScriptInfo>, ServerError> {
let mut conn = self.get_connection().await?;
let scripts = sqlx::query_file!("sql/select/scripts/all.sql")
.fetch_all(&mut *conn)
.await
.map_err(|err| {
log::error!("{}", t!("Database error: %{err}", err = err : {:?}));
ServerError::DB(DBError::Sqlx(err))
})?;
Ok(scripts
.into_iter()
.map(|r| ScriptInfo {
id: r.id,
name: r.script_name,
size: r.size.unwrap_or(0),
enabled: r.enabled,
})
.collect())
pub async fn get_script(&self, id: Uuid) -> Result<ScriptDetail, ServerError> {
let script = sqlx::query_file!("sql/select/scripts/by_id.sql", &id)
.fetch_one(&mut *conn)
Ok(ScriptDetail {
id: script.id,
name: script.script_name,
bytecode: script.bytecode,
enabled: script.enabled,
pub async fn create_script(
&self,
bytecode: Vec<u8>,
name: Option<String>,
) -> Result<Uuid, ServerError> {
let id = Uuid::new_v4();
sqlx::query_file!("sql/insert/scripts/script.sql", &id, &bytecode)
.execute(&mut *conn)
if let Some(name) = name {
let tag_id = Uuid::new_v4();
Tag {
id: tag_id,
tag_name: "name".to_string(),
tag_value: name,
description: None,
.commit(&mut *conn)
ServerError::Finance(err)
sqlx::query_file!("sql/insert/script_tags/script_tag.sql", &id, &tag_id)
Ok(id)
pub async fn update_script_bytecode(
id: Uuid,
) -> Result<(), ServerError> {
sqlx::query_file!("sql/update/scripts/bytecode.sql", &id, &bytecode)
Ok(())
pub async fn delete_script(&self, id: Uuid) -> Result<(), ServerError> {
sqlx::query!("DELETE FROM script_tags WHERE script_id = $1", &id)
sqlx::query_file!("sql/delete/scripts/by_id.sql", &id)
pub async fn set_script_enabled(&self, id: Uuid, enabled: bool) -> Result<(), ServerError> {
sqlx::query!(
"DELETE FROM script_tags WHERE script_id = $1 AND tag_id IN (
SELECT t.id FROM tags t
INNER JOIN script_tags st ON st.tag_id = t.id
WHERE st.script_id = $1 AND t.tag_name = 'enabled'
)",
&id
)
if !enabled {
tag_name: "enabled".to_string(),
tag_value: "false".to_string(),
pub async fn update_script_name(
WHERE st.script_id = $1 AND t.tag_name = 'name'
#[cfg(test)]
mod script_mgmt_tests {
use super::*;
use crate::db::DB_POOL;
#[cfg(feature = "testlog")]
use env_logger;
use log;
use sqlx::PgPool;
use supp_macro::local_db_sqlx_test;
use tokio::sync::OnceCell;
static CONTEXT: OnceCell<()> = OnceCell::const_new();
static USER: OnceCell<User> = OnceCell::const_new();
async fn setup() {
CONTEXT
.get_or_init(|| async {
let _ = env_logger::builder()
.is_test(true)
.filter_level(log::LevelFilter::Trace)
.try_init();
.await;
USER.get_or_init(|| async { User { id: Uuid::new_v4() } })
#[local_db_sqlx_test]
async fn test_create_and_list_scripts(pool: PgPool) -> Result<(), anyhow::Error> {
USER.get()
.unwrap()
.commit()
.expect("Failed to commit user to database");
let user = USER.get().unwrap();
let bytecode = vec![0x00, 0x61, 0x73, 0x6d];
let id = user.create_script(bytecode.clone(), None).await?;
assert!(!id.is_nil());
let scripts = user.list_scripts().await?;
assert_eq!(scripts.len(), 1);
assert_eq!(scripts[0].id, id);
assert_eq!(scripts[0].size, bytecode.len() as i32);
assert!(scripts[0].name.is_none());
async fn test_create_script_with_name(pool: PgPool) -> Result<(), anyhow::Error> {
let name = "test_script".to_string();
let _id = user.create_script(bytecode, Some(name.clone())).await?;
assert_eq!(scripts[0].name, Some(name));
async fn test_get_script(pool: PgPool) -> Result<(), anyhow::Error> {
let bytecode = vec![0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00];
let name = "fetch_test".to_string();
let id = user
.create_script(bytecode.clone(), Some(name.clone()))
.await?;
let script = user.get_script(id).await?;
assert_eq!(script.id, id);
assert_eq!(script.bytecode, bytecode);
assert_eq!(script.name, Some(name));
async fn test_update_script_bytecode(pool: PgPool) -> Result<(), anyhow::Error> {
let original_bytecode = vec![0x00, 0x61, 0x73, 0x6d];
let updated_bytecode = vec![0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00];
let id = user.create_script(original_bytecode, None).await?;
user.update_script_bytecode(id, updated_bytecode.clone())
assert_eq!(script.bytecode, updated_bytecode);
async fn test_delete_script(pool: PgPool) -> Result<(), anyhow::Error> {
.create_script(bytecode, Some("to_delete".to_string()))
user.delete_script(id).await?;
assert!(scripts.is_empty());
async fn test_set_script_enabled(pool: PgPool) -> Result<(), anyhow::Error> {
let id = user.create_script(bytecode, None).await?;
assert!(script.enabled.is_none());
user.set_script_enabled(id, false).await?;
assert_eq!(script.enabled, Some("false".to_string()));
user.set_script_enabled(id, true).await?;
async fn test_update_script_name(pool: PgPool) -> Result<(), anyhow::Error> {
.create_script(bytecode, Some("original_name".to_string()))
assert_eq!(script.name, Some("original_name".to_string()));
user.update_script_name(id, Some("new_name".to_string()))
assert_eq!(script.name, Some("new_name".to_string()));
user.update_script_name(id, None).await?;
assert!(script.name.is_none());
async fn test_multiple_scripts(pool: PgPool) -> Result<(), anyhow::Error> {
let id1 = user
.create_script(vec![0x01], Some("script1".to_string()))
let id2 = user
.create_script(vec![0x02, 0x03], Some("script2".to_string()))
let id3 = user.create_script(vec![0x04, 0x05, 0x06], None).await?;
assert_eq!(scripts.len(), 3);
let ids: Vec<Uuid> = scripts.iter().map(|s| s.id).collect();
assert!(ids.contains(&id1));
assert!(ids.contains(&id2));
assert!(ids.contains(&id3));
user.delete_script(id2).await?;
assert_eq!(scripts.len(), 2);
assert!(!ids.contains(&id2));