Skip to content
This repository has been archived by the owner on Aug 21, 2024. It is now read-only.

Commit

Permalink
Deduplicate prod and test startup code
Browse files Browse the repository at this point in the history
  • Loading branch information
0rzech committed Feb 27, 2024
1 parent 65dd9f8 commit 21e238e
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 81 deletions.
28 changes: 3 additions & 25 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
use sqlx::PgPool;
use tokio::net::TcpListener;
use zero2prod::{
configuration::get_configuration,
email_client::EmailClient,
startup::run,
startup::Application,
telemetry::{get_subscriber, init_subscriber},
};

Expand All @@ -13,26 +10,7 @@ async fn main() -> Result<(), std::io::Error> {
init_subscriber(subscriber);

let config = get_configuration().expect("Failed to read configuration");
let address = format!("{}:{}", config.application.host, config.application.port);
let app = Application::build(config).await;

let listener = TcpListener::bind(address)
.await
.expect("Failed to open listener");

let pool = PgPool::connect_lazy_with(config.database.with_db());

let sender_email = config
.email_client
.sender()
.expect("Invalid sender email address");
let timeout = config.email_client.timeout();

let email_client = EmailClient::new(
config.email_client.base_url,
sender_email,
config.email_client.authorization_token,
timeout,
);

run(listener, pool, email_client).await
app.run_until_stopped().await
}
100 changes: 72 additions & 28 deletions src/startup.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use crate::{
app_state::AppState,
configuration::{DatabaseSettings, Settings},
email_client::EmailClient,
request_id::RequestUuid,
routes::{health_check, subscriptions},
telemetry::request_span,
};
use axum::Router;
use sqlx::PgPool;
use axum::{serve::Serve, Router};
use sqlx::{postgres::PgPoolOptions, PgPool};
use std::net::SocketAddr;
use tokio::net::TcpListener;
use tower::ServiceBuilder;
use tower_http::{
Expand All @@ -15,32 +17,74 @@ use tower_http::{
};
use tracing::Level;

pub async fn run(
listener: TcpListener,
db_pool: PgPool,
email_client: EmailClient,
) -> Result<(), std::io::Error> {
let app_state = AppState {
db_pool,
email_client,
};

let app = Router::new()
.merge(health_check::router())
.merge(subscriptions::router())
.with_state(app_state)
.layer(
ServiceBuilder::new()
.set_x_request_id(RequestUuid)
.layer(
TraceLayer::new_for_http()
.make_span_with(request_span)
.on_request(DefaultOnRequest::new().level(Level::INFO))
.on_response(DefaultOnResponse::new().level(Level::INFO)),
)
.propagate_x_request_id(),
pub struct Application {
local_addr: SocketAddr,
server: Serve<Router, Router>,
}

impl Application {
pub async fn build(config: Settings) -> Application {
let address = format!("{}:{}", config.application.host, config.application.port);

let listener = TcpListener::bind(address)
.await
.expect("Failed to open listener");

let sender_email = config
.email_client
.sender()
.expect("Invalid sender email address");
let timeout = config.email_client.timeout();

let email_client = EmailClient::new(
config.email_client.base_url,
sender_email,
config.email_client.authorization_token,
timeout,
);

tracing::info!("Listening on {}", listener.local_addr()?);
axum::serve(listener, app).await
let db_pool = get_connection_pool(&config.database);

let app_state = AppState {
db_pool,
email_client,
};

let app = Router::new()
.merge(health_check::router())
.merge(subscriptions::router())
.with_state(app_state)
.layer(
ServiceBuilder::new()
.set_x_request_id(RequestUuid)
.layer(
TraceLayer::new_for_http()
.make_span_with(request_span)
.on_request(DefaultOnRequest::new().level(Level::INFO))
.on_response(DefaultOnResponse::new().level(Level::INFO)),
)
.propagate_x_request_id(),
);

let local_addr = listener
.local_addr()
.expect("Failed to get local address from the listener");

let server = axum::serve(listener, app);

Self { local_addr, server }
}

pub fn local_addr(&self) -> SocketAddr {
self.local_addr
}

pub async fn run_until_stopped(self) -> Result<(), std::io::Error> {
tracing::info!("Listening on {}", self.local_addr);
self.server.await
}
}

pub fn get_connection_pool(config: &DatabaseSettings) -> PgPool {
PgPoolOptions::new().connect_lazy_with(config.with_db())
}
38 changes: 10 additions & 28 deletions tests/api/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ use std::net::SocketAddr;
use uuid::Uuid;
use zero2prod::{
configuration::{get_configuration, DatabaseSettings},
email_client::EmailClient,
startup::run,
startup::{get_connection_pool, Application},
telemetry::{get_subscriber, init_subscriber},
};

Expand All @@ -31,34 +30,19 @@ pub async fn spawn_app() -> TestApp {

let mut config = get_configuration().expect("Failed to read configuration");
config.database.database_name = Uuid::new_v4().to_string();
config.application.port = 0;

let listener = tokio::net::TcpListener::bind("localhost:0")
.await
.expect("Failed to bind address");
let db_pool = configure_database(&config.database).await;
let app = Application::build(config).await;

let app = TestApp {
address: listener.local_addr().expect("Failed to get local address"),
db_pool: configure_database(&config.database).await,
let test_app = TestApp {
address: app.local_addr(),
db_pool,
};

let pool = app.db_pool.clone();
let sender_email = config.email_client.sender().expect("Invalid sender email");
let timeout = config.email_client.timeout();

let email_client = EmailClient::new(
config.email_client.base_url,
sender_email,
config.email_client.authorization_token,
timeout,
);

tokio::spawn(async move {
run(listener, pool, email_client)
.await
.expect("Failed to run server");
});
tokio::spawn(async { app.run_until_stopped().await });

app
test_app
}

pub fn url(addr: SocketAddr, endpoint: &str) -> String {
Expand All @@ -74,9 +58,7 @@ async fn configure_database(configuration: &DatabaseSettings) -> PgPool {
.await
.expect("Failed to create database");

let pool = PgPool::connect_with(configuration.with_db())
.await
.expect("Failed to connect to Postgres");
let pool = get_connection_pool(&configuration);

sqlx::migrate!("./migrations")
.run(&pool)
Expand Down

0 comments on commit 21e238e

Please sign in to comment.