diff --git a/Cargo.toml b/Cargo.toml index 3881ade..4d8af53 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,8 @@ regex = "1.10.2" mockall = "0.11.4" bcrypt = "0.15.0" serde_json = "1.0.108" +sqlx = "0.7.3" +sqlx-postgres = "0.7.3" [build-dependencies] tonic-build = "0.10.2" diff --git a/src/api/ecdar_api.rs b/src/api/ecdar_api.rs index c5033f8..0120ae6 100644 --- a/src/api/ecdar_api.rs +++ b/src/api/ecdar_api.rs @@ -7,6 +7,7 @@ use std::sync::Arc; use tonic::{Code, Request, Response, Status}; use crate::api::auth::{RequestExt, Token, TokenType}; +use crate::api::server::server::component::Rep; use crate::database::{ access_context::AccessContextTrait, in_use_context::InUseContextTrait, model_context::ModelContextTrait, query_context::QueryContextTrait, @@ -18,7 +19,7 @@ use super::server::server::{ ecdar_api_server::EcdarApi, ecdar_backend_server::EcdarBackend, get_auth_token_request::{user_credentials, UserCredentials}, - CreateAccessRequest, CreateModelRequest, CreateModelResponse, CreateQueryRequest, + Component, CreateAccessRequest, CreateModelRequest, CreateModelResponse, CreateQueryRequest, CreateUserRequest, DeleteAccessRequest, DeleteModelRequest, DeleteQueryRequest, GetAuthTokenRequest, GetAuthTokenResponse, QueryRequest, QueryResponse, SimulationStartRequest, SimulationStepRequest, SimulationStepResponse, UpdateAccessRequest, UpdateQueryRequest, @@ -153,7 +154,25 @@ impl EcdarApi for ConcreteEcdarApi { match self.model_context.create(model).await { Ok(model) => Ok(Response::new(CreateModelResponse { id: model.id })), - Err(error) => Err(Status::internal(error.to_string())), + Err(error) => match error.sql_err() { + Some(SqlErr::UniqueConstraintViolation(e)) => { + let error_msg = match e.to_lowercase() { + _ if e.contains("name") => "A model with that name already exists", + _ => "Model already exists", + }; + println!("{}", e); + Err(Status::already_exists(error_msg)) + } + Some(SqlErr::ForeignKeyConstraintViolation(e)) => { + let error_msg = match e.to_lowercase() { + _ if e.contains("owner_id") => "No user with that id exists", + _ => "Could not create model", + }; + println!("{}", e); + Err(Status::invalid_argument(error_msg)) + } + _ => Err(Status::internal(error.to_string())), + }, } } @@ -563,3 +582,7 @@ mod tests; #[cfg(test)] #[path = "../tests/api/query_logic.rs"] mod query_logic; + +#[cfg(test)] +#[path = "../tests/api/model_logic.rs"] +mod model_logic; diff --git a/src/tests/api/model_logic.rs b/src/tests/api/model_logic.rs new file mode 100644 index 0000000..fbd20a4 --- /dev/null +++ b/src/tests/api/model_logic.rs @@ -0,0 +1,87 @@ +#[cfg(test)] +use crate::api::server::server::ecdar_api_server::EcdarApi; +use crate::api::server::server::{Component, ComponentsInfo, CreateModelRequest}; +use crate::entities::{model, user}; +use crate::tests::api::helpers::{get_mock_concrete_ecdar_api, get_mock_services}; +use mockall::predicate; +use sea_orm::DbErr; +use tonic::{Code, Request}; + +#[tokio::test] +async fn create_model_returns_ok() { + let mut mock_services = get_mock_services(); + + let uid = 0; + + let components_info = ComponentsInfo { + components: vec![], + components_hash: 0, + }; + + let model = model::Model { + id: Default::default(), + name: Default::default(), + components_info: serde_json::to_value(components_info.clone()).unwrap(), + owner_id: uid.clone(), + }; + + mock_services + .model_context_mock + .expect_create() + .with(predicate::eq(model.clone())) + .returning(move |_| Ok(model.clone())); + + let mut request = Request::new(CreateModelRequest { + name: Default::default(), + components_info: Option::from(components_info), + owner_id: uid.clone(), + }); + + request + .metadata_mut() + .insert("uid", uid.to_string().parse().unwrap()); + + println!("{:?}", request); + + let api = get_mock_concrete_ecdar_api(mock_services); + + let res = api.create_model(request).await; + + assert!(res.is_ok()); +} + +#[tokio::test] +async fn create_model_existing_name_returns_err() { + let mut mock_services = get_mock_services(); + + let uid = 0; + + let model = model::Model { + id: Default::default(), + name: "model".to_string(), + components_info: Default::default(), + owner_id: uid.clone(), + }; + + mock_services + .model_context_mock + .expect_create() + .with(predicate::eq(model.clone())) + .returning(move |_| Err(DbErr::RecordNotInserted)); //todo!("Needs to be a SqlError with UniqueConstraintViolation with 'name' in message) + + let mut request = Request::new(CreateModelRequest { + name: "model".to_string(), + components_info: Default::default(), + owner_id: uid.clone(), + }); + + request + .metadata_mut() + .insert("uid", uid.to_string().parse().unwrap()); + + let api = get_mock_concrete_ecdar_api(mock_services); + + let res = api.create_model(request).await; + + assert_eq!(res.unwrap_err().code(), Code::InvalidArgument); //todo!("Needs to be code AlreadyExists when mocked Error is corrected) +}