Skip to content

Commit

Permalink
Add a test that tests the /organizations endpoint. Also moves setup o…
Browse files Browse the repository at this point in the history
…f logging into its own module & struct service::logging::Logger
  • Loading branch information
jhodapp committed Dec 27, 2023
1 parent 3c1d94c commit ab4b91e
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 36 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion entity_api/src/organization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use sea_orm::{
};
use serde_json::json;

extern crate log;
use log::*;

pub async fn create(db: &DatabaseConnection, organization_model: Model) -> Result<Model, Error> {
Expand Down
1 change: 1 addition & 0 deletions service/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ features = [
[dependencies]
clap = { version = "4.4.6", features = ["cargo", "derive", "env"] }
log = "0.4"
simplelog = { version = "0.12", features = ["paris"] }
serde_json = "1.0.107"
tokio = { version = "1.35", features = ["full"] }
tower = "0.4.13"
1 change: 1 addition & 0 deletions service/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::sync::Arc;
use tokio::time::Duration;

pub mod config;
pub mod logging;

pub async fn init_database(mut app_state: AppState) -> Result<AppState, DbErr> {
let mut opt = ConnectOptions::new::<&str>(app_state.config.database_uri().as_ref());
Expand Down
28 changes: 28 additions & 0 deletions service/src/logging.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use crate::config::Config;
use log::LevelFilter;
use simplelog;

pub struct Logger {}

impl Logger {
pub fn init_logger(config: &Config) {
let log_level_filter = match config.log_level_filter {
LevelFilter::Off => simplelog::LevelFilter::Off,
LevelFilter::Error => simplelog::LevelFilter::Error,
LevelFilter::Warn => simplelog::LevelFilter::Warn,
LevelFilter::Info => simplelog::LevelFilter::Info,
LevelFilter::Debug => simplelog::LevelFilter::Debug,
LevelFilter::Trace => simplelog::LevelFilter::Trace,
};

simplelog::TermLogger::init(
log_level_filter,
simplelog::Config::default(),
simplelog::TerminalMode::Mixed,
simplelog::ColorChoice::Auto,
)
.expect("Failed to start simplelog");

simplelog::info!("<b>Starting up...</b>.");
}
}
39 changes: 8 additions & 31 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,12 @@
//! and/or teams by providing a single application that facilitates and enhances
//! your coaching practice.
use service::{config::Config, AppState};

use log::LevelFilter;

extern crate simplelog;
use service::{config::Config, logging::Logger, AppState};

#[tokio::main]
async fn main() {
let config = get_config();

init_logger(&config);
Logger::init_logger(&config);

let mut app_state = AppState::new(config);
app_state = service::init_database(app_state).await.unwrap();
Expand All @@ -47,39 +42,20 @@ fn get_config() -> Config {
Config::new()
}

fn init_logger(config: &Config) {
let log_level_filter = match config.log_level_filter {
LevelFilter::Off => simplelog::LevelFilter::Off,
LevelFilter::Error => simplelog::LevelFilter::Error,
LevelFilter::Warn => simplelog::LevelFilter::Warn,
LevelFilter::Info => simplelog::LevelFilter::Info,
LevelFilter::Debug => simplelog::LevelFilter::Debug,
LevelFilter::Trace => simplelog::LevelFilter::Trace,
};

simplelog::TermLogger::init(
log_level_filter,
simplelog::Config::default(),
simplelog::TerminalMode::Mixed,
simplelog::ColorChoice::Auto,
)
.expect("Failed to start simplelog");

simplelog::info!("<b>Starting up...</b>.");
}

// This is the parent test "runner" that initiates all other crate
// unit/integration tests.
#[cfg(test)]
mod all_tests {
pub mod all_tests {
use log::LevelFilter;
use service::logging::Logger;
use simplelog;
use std::process::Command;

#[tokio::test]
async fn main() {
let mut config = super::get_config();
let mut config = crate::get_config();
config.log_level_filter = LevelFilter::Trace;
super::init_logger(&config);
Logger::init_logger(&config);

let mut exit_codes = Vec::new();

Expand Down Expand Up @@ -108,6 +84,7 @@ mod all_tests {
}

simplelog::info!("{}", String::from_utf8_lossy(output.stdout.as_slice()));
simplelog::info!("{}", String::from_utf8_lossy(output.stderr.as_slice()));

exit_codes.push(output.status.code().unwrap());
}
Expand Down
4 changes: 3 additions & 1 deletion web/src/controller/organization_controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use entity::{organization, Id};
use entity_api::organization as OrganizationApi;
use serde_json::json;

extern crate log;
use log::*;

pub struct OrganizationController {}
Expand All @@ -17,8 +16,11 @@ impl OrganizationController {
/// --request GET \
/// http://localhost:4000/organizations
pub async fn index(State(app_state): State<AppState>) -> Result<impl IntoResponse, Error> {
debug!("GET all Organizations");
let organizations = OrganizationApi::find_all(app_state.db_conn_ref().unwrap()).await?;

debug!("Found Organizations: {:?}", organizations);

Ok(Json(organizations))
}

Expand Down
83 changes: 80 additions & 3 deletions web/src/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,27 @@ pub fn static_routes() -> Router {
mod organization_endpoints_tests {
use super::*;
use entity::organization;
use log::*;
use sea_orm::{DatabaseBackend, MockDatabase};
use serde_json::json;
use service::config::Config;
use service::{config::Config, logging::Logger};
use std::net::SocketAddr;
use tokio::net::TcpListener;

// Call this at the top of a test to enable TRACE level debug logging for
// the test itself, seaORM, and Axum
// TODO: It'd be awesome if we could pass a flag to enable this when running
// `cargo test`
fn _enable_test_logging() {
let mut config = service::config::Config::default();
config.log_level_filter = LevelFilter::Trace;
Logger::init_logger(&config);
}

// Purpose: adds an Organization instance to a mock DB and tests the API to successfully
// retrieve it as valid JSON.
// retrieve it by a specific ID and as expected and valid JSON.
#[tokio::test]
async fn read_returns_succesful_json_when_organization_exists() -> anyhow::Result<()> {
async fn read_returns_expected_json_for_specified_organization() -> anyhow::Result<()> {
let organizations = vec![vec![organization::Model {
id: 1,
name: "Organization One".to_owned(),
Expand Down Expand Up @@ -77,4 +88,70 @@ mod organization_endpoints_tests {

Ok(())
}

// Purpose: adds multiple Organization instances to a mock DB and tests the API to successfully
// retrieve all of them as expected and valid JSON without specifying any particular ID.
#[tokio::test]
async fn read_returns_all_organizations() -> anyhow::Result<()> {
// Note: for entity_api::organization::find_all() to be able to return
// the correct query_results for the assert_eq!() below, they must all
// be grouped together in the same inner vector.
let organizations = [vec![
organization::Model {
id: 1,
name: "Organization One".to_owned(),
},
organization::Model {
id: 2,
name: "Organization Two".to_owned(),
},
organization::Model {
id: 3,
name: "Organization Three".to_owned(),
},
]];

let db = MockDatabase::new(DatabaseBackend::Postgres)
.append_query_results(organizations.clone())
.into_connection();

let mut app_state = AppState::new(Config::default());
app_state.set_db_conn(db);

let router = define_routes(app_state);

let listener = TcpListener::bind("0.0.0.0:0".parse::<SocketAddr>()?).await?;
let addr = listener.local_addr()?;

tokio::spawn(async move {
axum::serve(listener, router).await.unwrap();
});

let client = reqwest::Client::new();

let url = format!("http://{addr}/organizations");

let response = client.get(url).send().await?.text().await?;

let organization_model1 = &organizations[0][0];
let organization_model2 = &organizations[0][1];
let organization_model3 = &organizations[0][2];

assert_eq!(
response,
json!([{
"id": organization_model1.id,
"name": organization_model1.name,
},{
"id": organization_model2.id,
"name": organization_model2.name,
},{
"id": organization_model3.id,
"name": organization_model3.name,
}])
.to_string()
);

Ok(())
}
}

0 comments on commit ab4b91e

Please sign in to comment.