parent
b2fccdfa29
commit
e1b5d593f5
@ -0,0 +1,62 @@
|
|||||||
|
---
|
||||||
|
source: web/src/endpoints/vehicle/get_edit.rs
|
||||||
|
expression: body
|
||||||
|
snapshot_kind: text
|
||||||
|
---
|
||||||
|
<section class="section">
|
||||||
|
<div class="container"><form method="post" action="/vehicles/1">
|
||||||
|
<h1 class="title">Fahrzeug '11.49.1' bearbeiten</h1>
|
||||||
|
|
||||||
|
<div class="field is-horizontal">
|
||||||
|
<div class="field-label">
|
||||||
|
<label class="label">Funkkenner</label>
|
||||||
|
</div>
|
||||||
|
<div class="field-body">
|
||||||
|
<div class="field">
|
||||||
|
<div class="control">
|
||||||
|
<input class="input" type="text" name="radio_call_name" required placeholder="11.49.1" value="11.49.1" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field is-horizontal">
|
||||||
|
<div class="field-label">
|
||||||
|
<label class="label">Wache</label>
|
||||||
|
</div>
|
||||||
|
<div class="field-body">
|
||||||
|
<div class="field">
|
||||||
|
<div class="control">
|
||||||
|
<input class="input" type="text" name="station" required placeholder="FF Leipzig Ost" value="FF Leipzig Ost" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field is-horizontal">
|
||||||
|
<div class="field-label"></div>
|
||||||
|
<div class="field-body">
|
||||||
|
<div class="field is-grouped">
|
||||||
|
<div class="control">
|
||||||
|
<button class="button is-success">
|
||||||
|
<svg class="icon">
|
||||||
|
<use href="/static/feather-sprite.svg#check-circle" />
|
||||||
|
</svg>
|
||||||
|
<span>Speichern</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="control">
|
||||||
|
<a class="button is-link is-light" hx-boost="true" href="/vehicles">
|
||||||
|
<svg class="icon">
|
||||||
|
<use href="/static/feather-sprite.svg#arrow-left" />
|
||||||
|
</svg>
|
||||||
|
<span>Zurück</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</section>
|
@ -13,7 +13,7 @@ pub async fn get(
|
|||||||
pool: web::Data<PgPool>,
|
pool: web::Data<PgPool>,
|
||||||
path: web::Path<IdPath>,
|
path: web::Path<IdPath>,
|
||||||
) -> Result<impl Responder, ApplicationError> {
|
) -> Result<impl Responder, ApplicationError> {
|
||||||
if user.role != Role::Admin {
|
if user.role != Role::Admin && user.role != Role::AreaManager {
|
||||||
return Err(ApplicationError::Unauthorized);
|
return Err(ApplicationError::Unauthorized);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,3 +28,68 @@ pub async fn get(
|
|||||||
|
|
||||||
Ok(template.to_response()?)
|
Ok(template.to_response()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::{
|
||||||
|
models::{Function, Role, Vehicle},
|
||||||
|
utils::test_helper::{
|
||||||
|
assert_snapshot, read_body, test_get, DbTestContext, RequestConfig, StatusCode,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use brass_macros::db_test;
|
||||||
|
|
||||||
|
#[db_test]
|
||||||
|
async fn returns_not_found_for_user(context: &DbTestContext) {
|
||||||
|
Vehicle::create(&context.db_pool, "11.49.1", "FF Leipzig Ost")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let app = context.app().await;
|
||||||
|
|
||||||
|
let config = RequestConfig::new("/vehicles/1");
|
||||||
|
|
||||||
|
let response = test_get(&context.db_pool, &app, &config).await;
|
||||||
|
assert_eq!(StatusCode::UNAUTHORIZED, response.status());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[db_test]
|
||||||
|
async fn area_manager_can_edit(context: &DbTestContext) {
|
||||||
|
Vehicle::create(&context.db_pool, "11.49.1", "FF Leipzig Ost")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let app = context.app().await;
|
||||||
|
|
||||||
|
let config = RequestConfig {
|
||||||
|
uri: "/vehicles/1".to_string(),
|
||||||
|
role: Role::AreaManager,
|
||||||
|
function: vec![Function::Posten],
|
||||||
|
user_area: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let response = test_get(&context.db_pool, &app, &config).await;
|
||||||
|
assert_eq!(StatusCode::OK, response.status());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[db_test]
|
||||||
|
async fn produces_template_fine_when_user_is_admin(context: &DbTestContext) {
|
||||||
|
let app = context.app().await;
|
||||||
|
Vehicle::create(&context.db_pool, "11.49.1", "FF Leipzig Ost")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let config = RequestConfig {
|
||||||
|
uri: "/vehicles/1".to_string(),
|
||||||
|
role: Role::Admin,
|
||||||
|
function: vec![Function::Posten],
|
||||||
|
user_area: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let response = test_get(&context.db_pool, &app, &config).await;
|
||||||
|
assert_eq!(StatusCode::OK, response.status());
|
||||||
|
|
||||||
|
let body = read_body(response).await;
|
||||||
|
assert_snapshot!(body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -11,13 +11,19 @@ pub mod post_edit;
|
|||||||
pub mod post_new;
|
pub mod post_new;
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "vehicles/new_or_edit.html")]
|
#[cfg_attr(not(test), template(path = "vehicles/new_or_edit.html"))]
|
||||||
|
#[cfg_attr(
|
||||||
|
test,
|
||||||
|
template(path = "vehicles/new_or_edit.html", block = "content"),
|
||||||
|
allow(dead_code)
|
||||||
|
)]
|
||||||
pub struct VehicleNewOrEditTemplate {
|
pub struct VehicleNewOrEditTemplate {
|
||||||
user: User,
|
user: User,
|
||||||
vehicle: Option<Vehicle>,
|
vehicle: Option<Vehicle>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
|
#[cfg_attr(test, derive(serde::Serialize))]
|
||||||
pub struct VehicleForm {
|
pub struct VehicleForm {
|
||||||
radio_call_name: String,
|
radio_call_name: String,
|
||||||
station: String,
|
station: String,
|
||||||
|
@ -14,7 +14,7 @@ pub async fn post(
|
|||||||
path: web::Path<IdPath>,
|
path: web::Path<IdPath>,
|
||||||
form: web::Form<VehicleForm>,
|
form: web::Form<VehicleForm>,
|
||||||
) -> Result<impl Responder, ApplicationError> {
|
) -> Result<impl Responder, ApplicationError> {
|
||||||
if user.role != Role::Admin {
|
if user.role != Role::Admin && user.role != Role::AreaManager {
|
||||||
return Err(ApplicationError::Unauthorized);
|
return Err(ApplicationError::Unauthorized);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,3 +37,80 @@ pub async fn post(
|
|||||||
.insert_header(("HX-LOCATION", "/vehicles"))
|
.insert_header(("HX-LOCATION", "/vehicles"))
|
||||||
.finish())
|
.finish())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use actix_http::StatusCode;
|
||||||
|
use brass_macros::db_test;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
endpoints::vehicle::VehicleForm,
|
||||||
|
models::{Role, Vehicle},
|
||||||
|
utils::test_helper::{test_post, DbTestContext, RequestConfig},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[db_test]
|
||||||
|
async fn updates_vehicle_when_user_is_admin_and_vehicle_exists(context: &DbTestContext) {
|
||||||
|
works_for_role(context, Role::Admin).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[db_test]
|
||||||
|
async fn updates_vehicle_when_user_is_area_manager_and_vehicle_exists(context: &DbTestContext) {
|
||||||
|
works_for_role(context, Role::AreaManager).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn works_for_role(context: &DbTestContext, role: Role) {
|
||||||
|
Vehicle::create(&context.db_pool, "11.49.1", "FF Leipzig Ost")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let app = context.app().await;
|
||||||
|
|
||||||
|
let config = RequestConfig::new("/vehicles/1").with_role(role);
|
||||||
|
|
||||||
|
let request = VehicleForm {
|
||||||
|
station: "FF Leipzig Ost".to_string(),
|
||||||
|
radio_call_name: "11.49.2".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let response = test_post(&context.db_pool, app, &config, request).await;
|
||||||
|
|
||||||
|
assert_eq!(StatusCode::FOUND, response.status());
|
||||||
|
|
||||||
|
let updated_vehicle = Vehicle::read(&context.db_pool, 1).await.unwrap().unwrap();
|
||||||
|
|
||||||
|
assert_eq!("11.49.2".to_string(), updated_vehicle.radio_call_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[db_test]
|
||||||
|
async fn returns_unauthorized_when_user_is_staff(context: &DbTestContext) {
|
||||||
|
let app = context.app().await;
|
||||||
|
|
||||||
|
let config = RequestConfig::new("/vehicles/1");
|
||||||
|
|
||||||
|
let request = VehicleForm {
|
||||||
|
station: "FF Leipzig Ost".to_string(),
|
||||||
|
radio_call_name: "11.49.2".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let response = test_post(&context.db_pool, app, &config, request).await;
|
||||||
|
|
||||||
|
assert_eq!(StatusCode::UNAUTHORIZED, response.status());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[db_test]
|
||||||
|
async fn returns_not_found_when_vehicle_does_not_exist(context: &DbTestContext) {
|
||||||
|
let app = context.app().await;
|
||||||
|
|
||||||
|
let config = RequestConfig::new("/vehicles/1").with_role(Role::Admin);
|
||||||
|
|
||||||
|
let request = VehicleForm {
|
||||||
|
station: "FF Leipzig Ost".to_string(),
|
||||||
|
radio_call_name: "11.49.2".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let response = test_post(&context.db_pool, app, &config, request).await;
|
||||||
|
|
||||||
|
assert_eq!(StatusCode::NOT_FOUND, response.status());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -29,6 +29,24 @@ impl RequestConfig {
|
|||||||
user_area: 1,
|
user_area: 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_role(self, role: Role) -> Self {
|
||||||
|
let mut obj = self;
|
||||||
|
obj.role = role;
|
||||||
|
obj
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_functions(self, functions: &[Function]) -> Self {
|
||||||
|
let mut obj = self;
|
||||||
|
obj.function = functions.to_vec();
|
||||||
|
obj
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_user_area(self, user_area: i32) -> Self {
|
||||||
|
let mut obj = self;
|
||||||
|
obj.user_area = user_area;
|
||||||
|
obj
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_user_and_get_login_cookie<'a, T, R>(
|
async fn create_user_and_get_login_cookie<'a, T, R>(
|
||||||
|
@ -3,13 +3,13 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
{% if vehicle.is_some() %}
|
{%- if vehicle.is_some() -%}
|
||||||
<form method="post" action="/vehicles/{{ vehicle.as_ref().unwrap().id }}">
|
<form method="post" action="/vehicles/{{ vehicle.as_ref().unwrap().id }}">
|
||||||
<h1 class="title">Fahrzeug '{{ vehicle.as_ref().unwrap().radio_call_name }}' bearbeiten</h1>
|
<h1 class="title">Fahrzeug '{{ vehicle.as_ref().unwrap().radio_call_name }}' bearbeiten</h1>
|
||||||
{% else %}
|
{%- else -%}
|
||||||
<form method="post" action="/vehicles/new">
|
<form method="post" action="/vehicles/new">
|
||||||
<h1 class="title">Neues Fahrzeug anlegen</h1>
|
<h1 class="title">Neues Fahrzeug anlegen</h1>
|
||||||
{% endif %}
|
{%- endif %}
|
||||||
|
|
||||||
<div class="field is-horizontal">
|
<div class="field is-horizontal">
|
||||||
<div class="field-label">
|
<div class="field-label">
|
||||||
@ -49,11 +49,11 @@
|
|||||||
<use href="/static/feather-sprite.svg#check-circle" />
|
<use href="/static/feather-sprite.svg#check-circle" />
|
||||||
</svg>
|
</svg>
|
||||||
<span>
|
<span>
|
||||||
{% if vehicle.is_some() %}
|
{%- if vehicle.is_some() -%}
|
||||||
Speichern
|
Speichern
|
||||||
{% else %}
|
{%- else -%}
|
||||||
Erstellen
|
Erstellen
|
||||||
{% endif %}
|
{%- endif -%}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user