From 06421ed5455285eb5d5eb90ea689fa73ad0f3010 Mon Sep 17 00:00:00 2001 From: rtkay123 Date: Thu, 21 Aug 2025 17:59:13 +0200 Subject: tests: cov (#13) --- crates/configuration/src/server/http_svc.rs | 44 ++++ crates/configuration/src/server/http_svc/routes.rs | 14 ++ .../server/http_svc/routes/routing/get_active.rs | 52 +++++ .../server/http_svc/routes/routing/post_routing.rs | 99 +++++++++ .../src/server/http_svc/routes/rule.rs | 182 +++++++++++++++++ .../src/server/http_svc/routes/rule/create.rs | 90 +++++++++ .../src/server/http_svc/routes/rule/delete.rs | 102 ++++++++++ .../src/server/http_svc/routes/rule/get.rs | 113 +++++++++++ .../src/server/http_svc/routes/rule/update.rs | 133 +++++++++++- .../src/server/http_svc/routes/typology.rs | 224 +++++++++++++++++++++ .../http_svc/routes/typology/create_typology.rs | 103 ++++++++++ .../http_svc/routes/typology/delete_typology.rs | 116 +++++++++++ .../http_svc/routes/typology/get_typology.rs | 126 ++++++++++++ .../http_svc/routes/typology/post_typology.rs | 175 +++++++++++++++- crates/configuration/src/state/rule/mutate_rule.rs | 4 +- .../src/state/typology/mutate_typology.rs | 4 +- 16 files changed, 1572 insertions(+), 9 deletions(-) (limited to 'crates/configuration/src') diff --git a/crates/configuration/src/server/http_svc.rs b/crates/configuration/src/server/http_svc.rs index 423d67d..2997e13 100644 --- a/crates/configuration/src/server/http_svc.rs +++ b/crates/configuration/src/server/http_svc.rs @@ -63,3 +63,47 @@ pub fn build_router(state: AppHandle) -> Router { warden_middleware::apply(router) } + +#[cfg(test)] +mod tests { + use axum::{ + body::Body, + http::{Request, StatusCode}, + }; + use sqlx::PgPool; + use tower::ServiceExt; + use warden_stack::cache::RedisManager; + + use crate::{ + server::http_svc::{build_router, routes::test_config}, + state::{AppState, Services}, + }; + + #[sqlx::test] + async fn health_check(pool: PgPool) { + let config = test_config(); + + let cache = RedisManager::new(&config.cache).await.unwrap(); + let client = async_nats::connect(&config.nats.hosts[0]).await.unwrap(); + let jetstream = async_nats::jetstream::new(client); + + let state = AppState::create( + Services { + postgres: pool, + cache, + jetstream, + }, + &test_config(), + ) + .await + .unwrap(); + let app = build_router(state); + + let response = app + .oneshot(Request::builder().uri("/").body(Body::empty()).unwrap()) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::OK); + } +} diff --git a/crates/configuration/src/server/http_svc/routes.rs b/crates/configuration/src/server/http_svc/routes.rs index 92f3184..64fc4c3 100644 --- a/crates/configuration/src/server/http_svc/routes.rs +++ b/crates/configuration/src/server/http_svc/routes.rs @@ -31,3 +31,17 @@ pub fn router(store: AppHandle) -> OpenApiRouter { )) .with_state(store) } + +#[cfg(test)] +pub(crate) fn test_config() -> warden_stack::Configuration { + use warden_stack::Configuration; + + let config_path = "warden-config.toml"; + + let config = config::Config::builder() + .add_source(config::File::new(config_path, config::FileFormat::Toml)) + .build() + .unwrap(); + + config.try_deserialize::().unwrap() +} diff --git a/crates/configuration/src/server/http_svc/routes/routing/get_active.rs b/crates/configuration/src/server/http_svc/routes/routing/get_active.rs index 4875a80..6974c6d 100644 --- a/crates/configuration/src/server/http_svc/routes/routing/get_active.rs +++ b/crates/configuration/src/server/http_svc/routes/routing/get_active.rs @@ -37,3 +37,55 @@ pub async fn active_routing( .into_inner(); Ok(axum::Json(config.configuration).into_response()) } + +#[cfg(test)] +mod tests { + use axum::{ + body::Body, + http::{Request, StatusCode}, + }; + use sqlx::PgPool; + use tower::ServiceExt; + use warden_stack::cache::RedisManager; + + use crate::{ + server::http_svc::{build_router, routes::test_config}, + state::{AppState, Services}, + }; + + #[sqlx::test] + async fn get_empty(pool: PgPool) { + let config = test_config(); + + let cache = RedisManager::new(&config.cache).await.unwrap(); + let client = async_nats::connect(&config.nats.hosts[0]).await.unwrap(); + let jetstream = async_nats::jetstream::new(client); + + let state = AppState::create( + Services { + postgres: pool, + cache, + jetstream, + }, + &test_config(), + ) + .await + .unwrap(); + let app = build_router(state); + + let response = app + .clone() + .oneshot( + Request::builder() + .method("GET") + .header("Content-Type", "application/json") + .uri("/api/v0/routing") + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::OK); + } +} diff --git a/crates/configuration/src/server/http_svc/routes/routing/post_routing.rs b/crates/configuration/src/server/http_svc/routes/routing/post_routing.rs index 3578b65..ce9ba37 100644 --- a/crates/configuration/src/server/http_svc/routes/routing/post_routing.rs +++ b/crates/configuration/src/server/http_svc/routes/routing/post_routing.rs @@ -37,3 +37,102 @@ pub async fn post_routing( Ok((axum::http::StatusCode::CREATED, axum::Json(response))) } + +#[cfg(test)] +mod tests { + use axum::{ + body::Body, + http::{Request, StatusCode}, + }; + use sqlx::PgPool; + use tower::ServiceExt; + use warden_stack::cache::RedisManager; + + use crate::{ + server::http_svc::{build_router, routes::test_config}, + state::{AppState, Services}, + }; + + #[sqlx::test] + async fn create_routing(pool: PgPool) { + let config = test_config(); + + let cache = RedisManager::new(&config.cache).await.unwrap(); + let client = async_nats::connect(&config.nats.hosts[0]).await.unwrap(); + let jetstream = async_nats::jetstream::new(client); + + let state = AppState::create( + Services { + postgres: pool, + cache, + jetstream, + }, + &test_config(), + ) + .await + .unwrap(); + let app = build_router(state); + + let routing = serde_json::json!({ + "active": true, + "name": "Public Network Map", + "version": "1.0.0", + "messages": [ + { + "id": "004", + "version": "1.0.0", + "tx_tp": "pacs.002.001.12", + "typologies": [ + { + "id": "999", + "version": "1.0.0", + "rules": [ + { + "id": "901", + "version": "1.0.0" + } + ] + } + ] + } + ] + }); + + let body = serde_json::to_vec(&routing).unwrap(); + + let response = app + .clone() + .oneshot( + Request::builder() + .method("POST") + .header("Content-Type", "application/json") + .uri("/api/v0/routing") + .body(Body::from(body)) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::CREATED); + + // should have an active one + let response = app + .clone() + .oneshot( + Request::builder() + .method("GET") + .header("Content-Type", "application/json") + .uri("/api/v0/routing") + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::OK); + + // let bytes = axum::body::to_bytes(response.into_body(), usize::MAX).await.unwrap(); + // let routing_info: RoutingConfiguration = serde_json::from_slice(&bytes).unwrap(); + // + } +} diff --git a/crates/configuration/src/server/http_svc/routes/rule.rs b/crates/configuration/src/server/http_svc/routes/rule.rs index f4b0d33..597693f 100644 --- a/crates/configuration/src/server/http_svc/routes/rule.rs +++ b/crates/configuration/src/server/http_svc/routes/rule.rs @@ -2,3 +2,185 @@ pub mod create; pub mod delete; pub mod get; pub mod update; + +#[cfg(test)] +mod tests { + use axum::{ + body::{self, Body}, + http::{Request, StatusCode}, + }; + use sqlx::PgPool; + use tower::ServiceExt; + use warden_core::configuration::rule::RuleConfiguration; + use warden_stack::cache::RedisManager; + + use crate::{ + server::http_svc::{build_router, routes::test_config}, + state::{AppState, Services}, + }; + + #[sqlx::test] + async fn all_operations(pool: PgPool) { + let config = test_config(); + + let cache = RedisManager::new(&config.cache).await.unwrap(); + let client = async_nats::connect(&config.nats.hosts[0]).await.unwrap(); + let jetstream = async_nats::jetstream::new(client); + + let state = AppState::create( + Services { + postgres: pool, + cache, + jetstream, + }, + &test_config(), + ) + .await + .unwrap(); + + let app = build_router(state); + + let rule = serde_json::json!({ + "id": "901", + "version": "1.0.0", + "description": "Number of outgoing transactions - debtor", + "configuration": { + "parameters": { + "max_query_range": 86400000 + }, + "exit_conditions": [ + { + "sub_rule_ref": ".x00", + "reason": "Incoming transaction is unsuccessful" + } + ], + "bands": [ + { + "sub_rule_ref": ".01", + "upper_limit": 2, + "reason": "The debtor has performed one transaction to date" + }, + { + "sub_rule_ref": ".02", + "lower_limit": 2, + "upper_limit": 3, + "reason": "The debtor has performed two transactions to date" + }, + { + "sub_rule_ref": ".03", + "lower_limit": 3, + "reason": "The debtor has performed three or more transactions to date" + } + ] + } + }); + + let body = serde_json::to_vec(&rule).unwrap(); + + let response = app + .clone() + .oneshot( + Request::builder() + .method("POST") + .header("Content-Type", "application/json") + .uri("/api/v0/rule") + .body(Body::from(body)) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::CREATED); + + let response = app + .clone() + .oneshot( + Request::builder() + .method("GET") + .header("Content-Type", "application/json") + .uri("/api/v0/rule?id=901&version=1.0.0") + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + + let body = body::to_bytes(response.into_body(), usize::MAX) + .await + .unwrap(); + + let config: RuleConfiguration = serde_json::from_slice(&body).unwrap(); + + assert_eq!(&config.id, "901"); + assert_eq!(&config.version, "1.0.0"); + + let rule = serde_json::json!({ + "id": "902", + "version": "1.0.0", + "description": "Number of outgoing transactions - debtor", + "configuration": { + "parameters": { + "max_query_range": 86400000 + }, + "exit_conditions": [ + { + "sub_rule_ref": ".x00", + "reason": "Incoming transaction is unsuccessful" + } + ], + "bands": [] + } + }); + + let body = serde_json::to_vec(&rule).unwrap(); + + app.clone() + .oneshot( + Request::builder() + .method("PUT") + .header("Content-Type", "application/json") + .uri("/api/v0/rule?id=901&version=1.0.0") + .body(Body::from(body)) + .unwrap(), + ) + .await + .unwrap(); + + let response = app + .clone() + .oneshot( + Request::builder() + .method("GET") + .header("Content-Type", "application/json") + .uri("/api/v0/rule?id=902&version=1.0.0") + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + + let body = body::to_bytes(response.into_body(), usize::MAX) + .await + .unwrap(); + + let config: RuleConfiguration = serde_json::from_slice(&body).unwrap(); + + assert_eq!(&config.id, "902"); + assert!(&config.configuration.unwrap().bands.is_empty()); + + let response = app + .clone() + .oneshot( + Request::builder() + .method("DELETE") + .header("Content-Type", "application/json") + .uri("/api/v0/rule?id=902&version=1.0.0") + .body(Body::from(body)) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::OK); + } +} diff --git a/crates/configuration/src/server/http_svc/routes/rule/create.rs b/crates/configuration/src/server/http_svc/routes/rule/create.rs index 809c00b..f7aba5b 100644 --- a/crates/configuration/src/server/http_svc/routes/rule/create.rs +++ b/crates/configuration/src/server/http_svc/routes/rule/create.rs @@ -36,3 +36,93 @@ pub async fn create_rule( .into_inner(); Ok((axum::http::StatusCode::CREATED, axum::Json(response))) } + +#[cfg(test)] +mod tests { + use axum::{ + body::Body, + http::{Request, StatusCode}, + }; + use sqlx::PgPool; + use tower::ServiceExt; + use warden_stack::cache::RedisManager; + + use crate::{ + server::http_svc::{build_router, routes::test_config}, + state::{AppState, Services}, + }; + + #[sqlx::test] + async fn post_rule(pool: PgPool) { + let config = test_config(); + + let cache = RedisManager::new(&config.cache).await.unwrap(); + let client = async_nats::connect(&config.nats.hosts[0]).await.unwrap(); + let jetstream = async_nats::jetstream::new(client); + + let state = AppState::create( + Services { + postgres: pool, + cache, + jetstream, + }, + &test_config(), + ) + .await + .unwrap(); + + let app = build_router(state); + + let rule = serde_json::json!({ + "id": "901", + "version": "1.0.0", + "description": "Number of outgoing transactions - debtor", + "configuration": { + "parameters": { + "max_query_range": 86400000 + }, + "exit_conditions": [ + { + "sub_rule_ref": ".x00", + "reason": "Incoming transaction is unsuccessful" + } + ], + "bands": [ + { + "sub_rule_ref": ".01", + "upper_limit": 2, + "reason": "The debtor has performed one transaction to date" + }, + { + "sub_rule_ref": ".02", + "lower_limit": 2, + "upper_limit": 3, + "reason": "The debtor has performed two transactions to date" + }, + { + "sub_rule_ref": ".03", + "lower_limit": 3, + "reason": "The debtor has performed three or more transactions to date" + } + ] + } + }); + + let body = serde_json::to_vec(&rule).unwrap(); + + let response = app + .clone() + .oneshot( + Request::builder() + .method("POST") + .header("Content-Type", "application/json") + .uri("/api/v0/rule") + .body(Body::from(body)) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::CREATED); + } +} diff --git a/crates/configuration/src/server/http_svc/routes/rule/delete.rs b/crates/configuration/src/server/http_svc/routes/rule/delete.rs index 2352fba..0182e47 100644 --- a/crates/configuration/src/server/http_svc/routes/rule/delete.rs +++ b/crates/configuration/src/server/http_svc/routes/rule/delete.rs @@ -40,3 +40,105 @@ pub async fn delete_rule_config( Ok(axum::Json(body)) } + +#[cfg(test)] +mod tests { + use axum::{ + body::Body, + http::{Request, StatusCode}, + }; + use sqlx::PgPool; + use tower::ServiceExt; + use warden_stack::cache::RedisManager; + + use crate::{ + server::http_svc::{build_router, routes::test_config}, + state::{AppState, Services}, + }; + + #[sqlx::test] + async fn delete_rule(pool: PgPool) { + let config = test_config(); + + let cache = RedisManager::new(&config.cache).await.unwrap(); + let client = async_nats::connect(&config.nats.hosts[0]).await.unwrap(); + let jetstream = async_nats::jetstream::new(client); + + let state = AppState::create( + Services { + postgres: pool, + cache, + jetstream, + }, + &test_config(), + ) + .await + .unwrap(); + + let app = build_router(state); + + let rule = serde_json::json!({ + "id": "901", + "version": "1.0.0", + "description": "Number of outgoing transactions - debtor", + "configuration": { + "parameters": { + "max_query_range": 86400000 + }, + "exit_conditions": [ + { + "sub_rule_ref": ".x00", + "reason": "Incoming transaction is unsuccessful" + } + ], + "bands": [ + { + "sub_rule_ref": ".01", + "upper_limit": 2, + "reason": "The debtor has performed one transaction to date" + }, + { + "sub_rule_ref": ".02", + "lower_limit": 2, + "upper_limit": 3, + "reason": "The debtor has performed two transactions to date" + }, + { + "sub_rule_ref": ".03", + "lower_limit": 3, + "reason": "The debtor has performed three or more transactions to date" + } + ] + } + }); + + let body = serde_json::to_vec(&rule).unwrap(); + + app.clone() + .oneshot( + Request::builder() + .method("POST") + .header("Content-Type", "application/json") + .uri("/api/v0/rule") + .body(Body::from(body)) + .unwrap(), + ) + .await + .unwrap(); + + let response = app + .clone() + .oneshot( + Request::builder() + .method("DELETE") + .header("Content-Type", "application/json") + .uri("/api/v0/rule?id=901&version=1.0.0") + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::OK); + } +} diff --git a/crates/configuration/src/server/http_svc/routes/rule/get.rs b/crates/configuration/src/server/http_svc/routes/rule/get.rs index 935eefb..eccab62 100644 --- a/crates/configuration/src/server/http_svc/routes/rule/get.rs +++ b/crates/configuration/src/server/http_svc/routes/rule/get.rs @@ -40,3 +40,116 @@ pub async fn get_rule( Ok(axum::Json(response)) } + +#[cfg(test)] +mod tests { + use axum::{ + body::{self, Body}, + http::{Request, StatusCode}, + }; + use sqlx::PgPool; + use tower::ServiceExt; + use warden_core::configuration::rule::RuleConfiguration; + use warden_stack::cache::RedisManager; + + use crate::{ + server::http_svc::{build_router, routes::test_config}, + state::{AppState, Services}, + }; + + #[sqlx::test] + async fn get(pool: PgPool) { + let config = test_config(); + + let cache = RedisManager::new(&config.cache).await.unwrap(); + let client = async_nats::connect(&config.nats.hosts[0]).await.unwrap(); + let jetstream = async_nats::jetstream::new(client); + + let state = AppState::create( + Services { + postgres: pool, + cache, + jetstream, + }, + &test_config(), + ) + .await + .unwrap(); + + let app = build_router(state); + + let rule = serde_json::json!({ + "id": "901", + "version": "1.0.0", + "description": "Number of outgoing transactions - debtor", + "configuration": { + "parameters": { + "max_query_range": 86400000 + }, + "exit_conditions": [ + { + "sub_rule_ref": ".x00", + "reason": "Incoming transaction is unsuccessful" + } + ], + "bands": [ + { + "sub_rule_ref": ".01", + "upper_limit": 2, + "reason": "The debtor has performed one transaction to date" + }, + { + "sub_rule_ref": ".02", + "lower_limit": 2, + "upper_limit": 3, + "reason": "The debtor has performed two transactions to date" + }, + { + "sub_rule_ref": ".03", + "lower_limit": 3, + "reason": "The debtor has performed three or more transactions to date" + } + ] + } + }); + + let body = serde_json::to_vec(&rule).unwrap(); + + let response = app + .clone() + .oneshot( + Request::builder() + .method("POST") + .header("Content-Type", "application/json") + .uri("/api/v0/rule") + .body(Body::from(body)) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::CREATED); + + let response = app + .clone() + .oneshot( + Request::builder() + .method("GET") + .header("Content-Type", "application/json") + .uri("/api/v0/rule?id=901&version=1.0.0") + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + + let body = body::to_bytes(response.into_body(), usize::MAX) + .await + .unwrap(); + + let config: RuleConfiguration = serde_json::from_slice(&body).unwrap(); + + assert_eq!(&config.id, "901"); + assert_eq!(&config.version, "1.0.0"); + } +} diff --git a/crates/configuration/src/server/http_svc/routes/rule/update.rs b/crates/configuration/src/server/http_svc/routes/rule/update.rs index 7bf3fe0..2f61bcb 100644 --- a/crates/configuration/src/server/http_svc/routes/rule/update.rs +++ b/crates/configuration/src/server/http_svc/routes/rule/update.rs @@ -1,7 +1,8 @@ -use axum::extract::State; +use axum::extract::{Query, State}; use tonic::IntoRequest; use warden_core::configuration::rule::{ - RuleConfiguration, UpdateRuleRequest, mutate_rule_configuration_server::MutateRuleConfiguration, + RuleConfiguration, RuleConfigurationRequest, UpdateRuleRequest, + mutate_rule_configuration_server::MutateRuleConfiguration, }; use crate::{ @@ -15,6 +16,7 @@ use crate::{ path = "/{version}/rule", params( ("version" = Version, Path, description = "API version, e.g., v1, v2, v3"), + RuleConfigurationRequest ), responses(( status = OK, @@ -22,18 +24,21 @@ use crate::{ )), operation_id = "update rule configuration", // https://github.com/juhaku/utoipa/issues/1170 tag = TAG_RULES, - ) +) ] #[axum::debug_handler] #[tracing::instrument(skip(state))] pub async fn update_rule_config( version: Version, + Query(params): Query, State(state): State, axum::Json(body): axum::Json, ) -> Result, AppError> { let config = state .update_rule_configuration( UpdateRuleRequest { + id: params.id, + version: params.version, configuration: Some(body), } .into_request(), @@ -43,3 +48,125 @@ pub async fn update_rule_config( Ok(axum::Json(config)) } + +#[cfg(test)] +mod tests { + use axum::{ + body::Body, + http::{Request, StatusCode}, + }; + use sqlx::PgPool; + use tower::ServiceExt; + use warden_stack::cache::RedisManager; + + use crate::{ + server::http_svc::{build_router, routes::test_config}, + state::{AppState, Services}, + }; + + #[sqlx::test] + async fn update(pool: PgPool) { + let config = test_config(); + + let cache = RedisManager::new(&config.cache).await.unwrap(); + let client = async_nats::connect(&config.nats.hosts[0]).await.unwrap(); + let jetstream = async_nats::jetstream::new(client); + + let state = AppState::create( + Services { + postgres: pool, + cache, + jetstream, + }, + &test_config(), + ) + .await + .unwrap(); + + let app = build_router(state); + + let rule = serde_json::json!({ + "id": "901", + "version": "1.0.0", + "description": "Number of outgoing transactions - debtor", + "configuration": { + "parameters": { + "max_query_range": 86400000 + }, + "exit_conditions": [ + { + "sub_rule_ref": ".x00", + "reason": "Incoming transaction is unsuccessful" + } + ], + "bands": [ + { + "sub_rule_ref": ".01", + "upper_limit": 2, + "reason": "The debtor has performed one transaction to date" + }, + { + "sub_rule_ref": ".02", + "lower_limit": 2, + "upper_limit": 3, + "reason": "The debtor has performed two transactions to date" + }, + { + "sub_rule_ref": ".03", + "lower_limit": 3, + "reason": "The debtor has performed three or more transactions to date" + } + ] + } + }); + + let body = serde_json::to_vec(&rule).unwrap(); + + app.clone() + .oneshot( + Request::builder() + .method("POST") + .header("Content-Type", "application/json") + .uri("/api/v0/rule") + .body(Body::from(body)) + .unwrap(), + ) + .await + .unwrap(); + + let rule = serde_json::json!({ + "id": "902", + "version": "1.0.0", + "description": "Number of outgoing transactions - debtor", + "configuration": { + "parameters": { + "max_query_range": 86400000 + }, + "exit_conditions": [ + { + "sub_rule_ref": ".x00", + "reason": "Incoming transaction is unsuccessful" + } + ], + "bands": [] + } + }); + + let body = serde_json::to_vec(&rule).unwrap(); + + let response = app + .clone() + .oneshot( + Request::builder() + .method("PUT") + .header("Content-Type", "application/json") + .uri("/api/v0/rule?id=901&version=1.0.0") + .body(Body::from(body)) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::OK); + } +} diff --git a/crates/configuration/src/server/http_svc/routes/typology.rs b/crates/configuration/src/server/http_svc/routes/typology.rs index 85a593b..c2fa270 100644 --- a/crates/configuration/src/server/http_svc/routes/typology.rs +++ b/crates/configuration/src/server/http_svc/routes/typology.rs @@ -2,3 +2,227 @@ pub mod create_typology; pub mod delete_typology; pub mod get_typology; pub mod post_typology; + +#[cfg(test)] +mod tests { + use axum::{ + body::{self, Body}, + http::{Request, StatusCode}, + }; + use sqlx::PgPool; + use tower::ServiceExt; + use warden_core::configuration::typology::TypologyConfiguration; + use warden_stack::cache::RedisManager; + + use crate::{ + server::http_svc::{build_router, routes::test_config}, + state::{AppState, Services}, + }; + + #[sqlx::test] + async fn all_operations(pool: PgPool) { + let config = test_config(); + + let cache = RedisManager::new(&config.cache).await.unwrap(); + let client = async_nats::connect(&config.nats.hosts[0]).await.unwrap(); + let jetstream = async_nats::jetstream::new(client); + + let state = AppState::create( + Services { + postgres: pool, + cache, + jetstream, + }, + &test_config(), + ) + .await + .unwrap(); + + let app = build_router(state); + + let typology = serde_json::json!({ + "description": "Test description", + "typology_name": "Rule-901-Typology-999", + "id": "999", + "version": "1.0.0", + "workflow": { + "alert_threshold": 200, + "interdiction_threshold": 400 + }, + "rules": [ + { + "id": "901", + "version": "1.0.0", + "wghts": [ + { + "ref": ".err", + "wght": 0 + }, + { + "ref": ".x00", + "wght": 100 + }, + { + "ref": ".01", + "wght": 100 + }, + { + "ref": ".02", + "wght": 200 + }, + { + "ref": ".03", + "wght": 400 + } + ] + } + ], + "expression": { + "operator": "ADD", + "terms": [ + { + "id": "901", + "version": "1.0.0" + } + ] + } + }); + + let body = serde_json::to_vec(&typology).unwrap(); + + let response = app + .clone() + .oneshot( + Request::builder() + .method("POST") + .header("Content-Type", "application/json") + .uri("/api/v0/typology") + .body(Body::from(body)) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::CREATED); + + let response = app + .clone() + .oneshot( + Request::builder() + .method("GET") + .header("Content-Type", "application/json") + .uri("/api/v0/typology?id=999&version=1.0.0") + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + + let body = body::to_bytes(response.into_body(), usize::MAX) + .await + .unwrap(); + + let config: TypologyConfiguration = serde_json::from_slice(&body).unwrap(); + + assert_eq!(&config.id, "999"); + assert_eq!(&config.version, "1.0.0"); + + let typology = serde_json::json!({ + "description": "Test description", + "typology_name": "Rule-901-Typology-999", + "id": "901", + "version": "1.0.0", + "workflow": { + "alert_threshold": 200, + "interdiction_threshold": 400 + }, + "rules": [ + { + "id": "901", + "version": "1.0.0", + "wghts": [ + { + "ref": ".err", + "wght": 0 + }, + { + "ref": ".x00", + "wght": 100 + }, + { + "ref": ".01", + "wght": 100 + }, + { + "ref": ".02", + "wght": 200 + }, + { + "ref": ".03", + "wght": 400 + } + ] + } + ], + "expression": { + "operator": "ADD", + "terms": [ + { + "id": "901", + "version": "1.0.0" + } + ] + } + }); + + let body = serde_json::to_vec(&typology).unwrap(); + + app.clone() + .oneshot( + Request::builder() + .method("PUT") + .header("Content-Type", "application/json") + .uri("/api/v0/typology?id=999&version=1.0.0") + .body(Body::from(body)) + .unwrap(), + ) + .await + .unwrap(); + + let response = app + .clone() + .oneshot( + Request::builder() + .method("GET") + .header("Content-Type", "application/json") + .uri("/api/v0/typology?id=901&version=1.0.0") + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + + let body = body::to_bytes(response.into_body(), usize::MAX) + .await + .unwrap(); + + let config: TypologyConfiguration = serde_json::from_slice(&body).unwrap(); + + assert_eq!(&config.id, "901"); + + let response = app + .clone() + .oneshot( + Request::builder() + .method("DELETE") + .header("Content-Type", "application/json") + .uri("/api/v0/typology?id=901&version=1.0.0") + .body(Body::from(body)) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::OK); + } +} diff --git a/crates/configuration/src/server/http_svc/routes/typology/create_typology.rs b/crates/configuration/src/server/http_svc/routes/typology/create_typology.rs index 9f4985a..8aeeeec 100644 --- a/crates/configuration/src/server/http_svc/routes/typology/create_typology.rs +++ b/crates/configuration/src/server/http_svc/routes/typology/create_typology.rs @@ -36,3 +36,106 @@ pub async fn create_typology( .into_inner(); Ok((axum::http::StatusCode::CREATED, axum::Json(response))) } + +#[cfg(test)] +mod tests { + use axum::{ + body::Body, + http::{Request, StatusCode}, + }; + use sqlx::PgPool; + use tower::ServiceExt; + use warden_stack::cache::RedisManager; + + use crate::{ + server::http_svc::{build_router, routes::test_config}, + state::{AppState, Services}, + }; + + #[sqlx::test] + async fn post_typology(pool: PgPool) { + let config = test_config(); + + let cache = RedisManager::new(&config.cache).await.unwrap(); + let client = async_nats::connect(&config.nats.hosts[0]).await.unwrap(); + let jetstream = async_nats::jetstream::new(client); + + let state = AppState::create( + Services { + postgres: pool, + cache, + jetstream, + }, + &test_config(), + ) + .await + .unwrap(); + + let app = build_router(state); + + let typology = serde_json::json!({ + "description": "Test description", + "typology_name": "Rule-901-Typology-999", + "id": "999", + "version": "1.0.0", + "workflow": { + "alert_threshold": 200, + "interdiction_threshold": 400 + }, + "rules": [ + { + "id": "901", + "version": "1.0.0", + "wghts": [ + { + "ref": ".err", + "wght": 0 + }, + { + "ref": ".x00", + "wght": 100 + }, + { + "ref": ".01", + "wght": 100 + }, + { + "ref": ".02", + "wght": 200 + }, + { + "ref": ".03", + "wght": 400 + } + ] + } + ], + "expression": { + "operator": "ADD", + "terms": [ + { + "id": "901", + "version": "1.0.0" + } + ] + } + }); + + let body = serde_json::to_vec(&typology).unwrap(); + + let response = app + .clone() + .oneshot( + Request::builder() + .method("POST") + .header("Content-Type", "application/json") + .uri("/api/v0/typology") + .body(Body::from(body)) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::CREATED); + } +} diff --git a/crates/configuration/src/server/http_svc/routes/typology/delete_typology.rs b/crates/configuration/src/server/http_svc/routes/typology/delete_typology.rs index 0e85e29..276aa67 100644 --- a/crates/configuration/src/server/http_svc/routes/typology/delete_typology.rs +++ b/crates/configuration/src/server/http_svc/routes/typology/delete_typology.rs @@ -39,3 +39,119 @@ pub async fn delete_typology( Ok(axum::Json(config)) } + +#[cfg(test)] +mod tests { + use axum::{ + body::Body, + http::{Request, StatusCode}, + }; + use sqlx::PgPool; + use tower::ServiceExt; + use warden_stack::cache::RedisManager; + + use crate::{ + server::http_svc::{build_router, routes::test_config}, + state::{AppState, Services}, + }; + + #[sqlx::test] + async fn delete_typology(pool: PgPool) { + let config = test_config(); + + let cache = RedisManager::new(&config.cache).await.unwrap(); + let client = async_nats::connect(&config.nats.hosts[0]).await.unwrap(); + let jetstream = async_nats::jetstream::new(client); + + let state = AppState::create( + Services { + postgres: pool, + cache, + jetstream, + }, + &test_config(), + ) + .await + .unwrap(); + + let app = build_router(state); + + let typology = serde_json::json!({ + "description": "Test description", + "typology_name": "Rule-901-Typology-999", + "id": "999", + "version": "1.0.0", + "workflow": { + "alert_threshold": 200, + "interdiction_threshold": 400 + }, + "rules": [ + { + "id": "901", + "version": "1.0.0", + "wghts": [ + { + "ref": ".err", + "wght": 0 + }, + { + "ref": ".x00", + "wght": 100 + }, + { + "ref": ".01", + "wght": 100 + }, + { + "ref": ".02", + "wght": 200 + }, + { + "ref": ".03", + "wght": 400 + } + ] + } + ], + "expression": { + "operator": "ADD", + "terms": [ + { + "id": "901", + "version": "1.0.0" + } + ] + } + + }); + + let body = serde_json::to_vec(&typology).unwrap(); + + app.clone() + .oneshot( + Request::builder() + .method("POST") + .header("Content-Type", "application/json") + .uri("/api/v0/typology") + .body(Body::from(body)) + .unwrap(), + ) + .await + .unwrap(); + + let response = app + .clone() + .oneshot( + Request::builder() + .method("DELETE") + .header("Content-Type", "application/json") + .uri("/api/v0/typology?id=999&version=1.0.0") + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::OK); + } +} diff --git a/crates/configuration/src/server/http_svc/routes/typology/get_typology.rs b/crates/configuration/src/server/http_svc/routes/typology/get_typology.rs index 4962593..40d3faf 100644 --- a/crates/configuration/src/server/http_svc/routes/typology/get_typology.rs +++ b/crates/configuration/src/server/http_svc/routes/typology/get_typology.rs @@ -38,3 +38,129 @@ pub async fn get_typology( Ok(axum::Json(config.configuration)) } + +#[cfg(test)] +mod tests { + use axum::{ + body::{self, Body}, + http::{Request, StatusCode}, + }; + use sqlx::PgPool; + use tower::ServiceExt; + use warden_core::configuration::typology::TypologyConfiguration; + use warden_stack::cache::RedisManager; + + use crate::{ + server::http_svc::{build_router, routes::test_config}, + state::{AppState, Services}, + }; + + #[sqlx::test] + async fn get(pool: PgPool) { + let config = test_config(); + + let cache = RedisManager::new(&config.cache).await.unwrap(); + let client = async_nats::connect(&config.nats.hosts[0]).await.unwrap(); + let jetstream = async_nats::jetstream::new(client); + + let state = AppState::create( + Services { + postgres: pool, + cache, + jetstream, + }, + &test_config(), + ) + .await + .unwrap(); + + let app = build_router(state); + + let typology = serde_json::json!({ + "description": "Test description", + "typology_name": "Rule-901-Typology-999", + "id": "999", + "version": "1.0.0", + "workflow": { + "alert_threshold": 200, + "interdiction_threshold": 400 + }, + "rules": [ + { + "id": "901", + "version": "1.0.0", + "wghts": [ + { + "ref": ".err", + "wght": 0 + }, + { + "ref": ".x00", + "wght": 100 + }, + { + "ref": ".01", + "wght": 100 + }, + { + "ref": ".02", + "wght": 200 + }, + { + "ref": ".03", + "wght": 400 + } + ] + } + ], + "expression": { + "operator": "ADD", + "terms": [ + { + "id": "901", + "version": "1.0.0" + } + ] + } + }); + + let body = serde_json::to_vec(&typology).unwrap(); + + let response = app + .clone() + .oneshot( + Request::builder() + .method("POST") + .header("Content-Type", "application/json") + .uri("/api/v0/typology") + .body(Body::from(body)) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::CREATED); + + let response = app + .clone() + .oneshot( + Request::builder() + .method("GET") + .header("Content-Type", "application/json") + .uri("/api/v0/typology?id=999&version=1.0.0") + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + + let body = body::to_bytes(response.into_body(), usize::MAX) + .await + .unwrap(); + + let config: TypologyConfiguration = serde_json::from_slice(&body).unwrap(); + + assert_eq!(&config.id, "999"); + assert_eq!(&config.version, "1.0.0"); + } +} diff --git a/crates/configuration/src/server/http_svc/routes/typology/post_typology.rs b/crates/configuration/src/server/http_svc/routes/typology/post_typology.rs index 2864372..0c432ab 100644 --- a/crates/configuration/src/server/http_svc/routes/typology/post_typology.rs +++ b/crates/configuration/src/server/http_svc/routes/typology/post_typology.rs @@ -1,6 +1,7 @@ -use axum::extract::State; +use axum::extract::{Query, State}; use warden_core::configuration::typology::{ - TypologyConfiguration, UpdateTypologyConfigRequest, mutate_typologies_server::MutateTypologies, + TypologyConfiguration, TypologyConfigurationRequest, UpdateTypologyConfigRequest, + mutate_typologies_server::MutateTypologies, }; use crate::{ @@ -14,6 +15,7 @@ use crate::{ path = "/{version}/typology", params( ("version" = Version, Path, description = "API version, e.g., v1, v2, v3"), + TypologyConfigurationRequest, ), responses(( status = OK, @@ -26,14 +28,183 @@ use crate::{ #[axum::debug_handler] #[tracing::instrument(skip(state))] pub async fn update( + version: Version, + Query(params): Query, State(state): State, axum::Json(body): axum::Json, ) -> Result, AppError> { let response = state .update_typology_configuration(tonic::Request::new(UpdateTypologyConfigRequest { + id: params.id, + version: params.version, configuration: Some(body), })) .await? .into_inner(); Ok(axum::Json(response)) } + +#[cfg(test)] +mod tests { + use axum::{ + body::Body, + http::{Request, StatusCode}, + }; + use sqlx::PgPool; + use tower::ServiceExt; + use warden_stack::cache::RedisManager; + + use crate::{ + server::http_svc::{build_router, routes::test_config}, + state::{AppState, Services}, + }; + + #[sqlx::test] + async fn update(pool: PgPool) { + let config = test_config(); + + let cache = RedisManager::new(&config.cache).await.unwrap(); + let client = async_nats::connect(&config.nats.hosts[0]).await.unwrap(); + let jetstream = async_nats::jetstream::new(client); + + let state = AppState::create( + Services { + postgres: pool, + cache, + jetstream, + }, + &test_config(), + ) + .await + .unwrap(); + + let app = build_router(state); + + let typology = serde_json::json!({ + "description": "Test description", + "typology_name": "Rule-901-Typology-999", + "id": "999", + "version": "1.0.0", + "workflow": { + "alert_threshold": 200, + "interdiction_threshold": 400 + }, + "rules": [ + { + "id": "901", + "version": "1.0.0", + "wghts": [ + { + "ref": ".err", + "wght": 0 + }, + { + "ref": ".x00", + "wght": 100 + }, + { + "ref": ".01", + "wght": 100 + }, + { + "ref": ".02", + "wght": 200 + }, + { + "ref": ".03", + "wght": 400 + } + ] + } + ], + "expression": { + "operator": "ADD", + "terms": [ + { + "id": "901", + "version": "1.0.0" + } + ] + } + }); + + let body = serde_json::to_vec(&typology).unwrap(); + + app.clone() + .oneshot( + Request::builder() + .method("POST") + .header("Content-Type", "application/json") + .uri("/api/v0/typology") + .body(Body::from(body)) + .unwrap(), + ) + .await + .unwrap(); + + let typology = serde_json::json!({ + "description": "Test description", + "typology_name": "Rule-901-Typology-999", + "id": "901", + "version": "1.0.0", + "workflow": { + "alert_threshold": 200, + "interdiction_threshold": 400 + }, + "rules": [ + { + "id": "901", + "version": "1.0.0", + "wghts": [ + { + "ref": ".err", + "wght": 0 + }, + { + "ref": ".x00", + "wght": 100 + }, + { + "ref": ".01", + "wght": 100 + }, + { + "ref": ".02", + "wght": 200 + }, + { + "ref": ".03", + "wght": 400 + } + ] + } + ], + "expression": { + "operator": "ADD", + "terms": [ + { + "id": "901", + "version": "1.0.0" + } + ] + } + }); + + let body = serde_json::to_vec(&typology).unwrap(); + + let response = app + .clone() + .oneshot( + Request::builder() + .method("PUT") + .header("Content-Type", "application/json") + .uri("/api/v0/typology?id=999&version=1.0.0") + .body(Body::from(body)) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::OK); + } +} diff --git a/crates/configuration/src/state/rule/mutate_rule.rs b/crates/configuration/src/state/rule/mutate_rule.rs index 9c4f393..10898b6 100644 --- a/crates/configuration/src/state/rule/mutate_rule.rs +++ b/crates/configuration/src/state/rule/mutate_rule.rs @@ -73,8 +73,8 @@ impl MutateRuleConfiguration for AppHandle { where id = $2 and version = $3 "#, sqlx::types::Json(&config) as _, - config.id, - config.id, + request.id, + request.version, ) .execute(&self.services.postgres) .instrument(span) diff --git a/crates/configuration/src/state/typology/mutate_typology.rs b/crates/configuration/src/state/typology/mutate_typology.rs index f2ab2cc..2673297 100644 --- a/crates/configuration/src/state/typology/mutate_typology.rs +++ b/crates/configuration/src/state/typology/mutate_typology.rs @@ -73,8 +73,8 @@ impl MutateTypologies for AppHandle { where id = $2 and version = $3 "#, sqlx::types::Json(&config) as _, - config.id, - config.version, + request.id, + request.version, ) .execute(&self.services.postgres) .instrument(span) -- cgit v1.2.3