summaryrefslogtreecommitdiff
path: root/src/tests.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/tests.rs')
-rw-r--r--src/tests.rs114
1 files changed, 107 insertions, 7 deletions
diff --git a/src/tests.rs b/src/tests.rs
index e97065a..9102975 100644
--- a/src/tests.rs
+++ b/src/tests.rs
@@ -1,9 +1,9 @@
use axum::{
Router,
- body::Body,
+ body::{Body, Bytes},
http::{
HeaderMap, Method, Request, StatusCode,
- header::{CONTENT_TYPE, LOCATION},
+ header::{CONTENT_DISPOSITION, CONTENT_TYPE, LOCATION},
},
};
use chrono::{TimeDelta, Utc};
@@ -21,6 +21,25 @@ async fn send(
uri: &str,
body: Option<String>,
) -> (StatusCode, HeaderMap, Value) {
+ let (status, headers, bytes) = send_raw(app, method, uri, body).await;
+ (
+ status,
+ headers,
+ serde_json::from_slice(&bytes).unwrap_or(Value::Null),
+ )
+}
+
+async fn get_text(app: &Router, uri: &str) -> (StatusCode, HeaderMap, String) {
+ let (status, headers, bytes) = send_raw(app, Method::GET, uri, None).await;
+ (status, headers, String::from_utf8(bytes.to_vec()).unwrap())
+}
+
+async fn send_raw(
+ app: &Router,
+ method: Method,
+ uri: &str,
+ body: Option<String>,
+) -> (StatusCode, HeaderMap, Bytes) {
let mut request = Request::builder().method(method).uri(uri);
let body = match body {
Some(body) => {
@@ -37,11 +56,7 @@ async fn send(
let status = response.status();
let headers = response.headers().clone();
let bytes = response.into_body().collect().await.unwrap().to_bytes();
- (
- status,
- headers,
- serde_json::from_slice(&bytes).unwrap_or(Value::Null),
- )
+ (status, headers, bytes)
}
async fn get_json(app: &Router, uri: &str) -> (StatusCode, Value) {
@@ -564,3 +579,88 @@ async fn update_rejects_future_measured_at(pool: PgPool) {
let (_, row) = get_json(&app, &uri).await;
assert_eq!(row["value"], json!(80.0));
}
+
+#[sqlx::test]
+async fn export_returns_a_csv_download(pool: PgPool) {
+ let app = router(pool);
+ let id = add_waist(&app, "2025-10-14T12:00:00Z", 80.5).await;
+ let (status, headers, csv) = get_text(&app, "/measurements/export").await;
+ assert_eq!(status, StatusCode::OK);
+ assert_eq!(headers[CONTENT_TYPE], "text/csv; charset=utf-8");
+ assert_eq!(
+ headers[CONTENT_DISPOSITION],
+ "attachment; filename=\"measurements.csv\""
+ );
+ assert_eq!(
+ csv,
+ format!(
+ "id,measured_at,metric_type,value,unit\n{id},2025-10-14T12:00:00Z,Waist,80.5,Centimeter\n"
+ )
+ );
+}
+
+#[sqlx::test]
+async fn export_without_data_is_just_the_header(pool: PgPool) {
+ let app = router(pool);
+ let (status, _, csv) = get_text(&app, "/measurements/export").await;
+ assert_eq!(status, StatusCode::OK);
+ assert_eq!(csv, "id,measured_at,metric_type,value,unit\n");
+}
+
+#[sqlx::test]
+async fn export_matches_the_list_for_the_same_query(pool: PgPool) {
+ let app = router(pool);
+ let rows = [
+ ("2025-10-14T12:00:00Z", "BodyWeight", 70.0, "Kilogram"),
+ ("2025-10-15T12:00:00Z", "Waist", 80.0, "Centimeter"),
+ ("2025-10-16T12:00:00Z", "BodyWeight", 71.0, "Kilogram"),
+ ("2025-10-17T12:00:00Z", "Hip", 95.0, "Centimeter"),
+ ];
+ for (measured_at, metric_type, value, unit) in rows {
+ let body = measurement(measured_at, metric_type, value, unit);
+ let (status, _, _) = send(&app, Method::POST, "/measurements", Some(body)).await;
+ assert_eq!(status, StatusCode::CREATED);
+ }
+ let queries = [
+ "",
+ "?metric_type=BodyWeight",
+ "?order=desc&limit=2",
+ "?from=2025-10-15T00:00:00Z&to=2025-10-17T00:00:00Z",
+ "?metric_type=Hip&from=2026-01-01T00:00:00Z",
+ ];
+ for query in queries {
+ let (_, listed) = get_json(&app, &format!("/measurements{query}")).await;
+ let listed: Vec<&str> = listed
+ .as_array()
+ .unwrap()
+ .iter()
+ .map(|row| row["id"].as_str().unwrap())
+ .collect();
+ let (status, _, csv) = get_text(&app, &format!("/measurements/export{query}")).await;
+ assert_eq!(status, StatusCode::OK, "{query}");
+ let exported: Vec<&str> = csv
+ .lines()
+ .skip(1)
+ .map(|line| line.split(',').next().unwrap())
+ .collect();
+ assert_eq!(exported, listed, "{query}");
+ }
+}
+
+#[sqlx::test]
+async fn export_rejects_bad_queries_with_json_errors(pool: PgPool) {
+ let app = router(pool);
+ let uris = [
+ "/measurements/export?limit=0",
+ "/measurements/export?metric_type=Nope",
+ "/measurements/export?date=2025-10-15",
+ "/measurements/export?from=2025-10-16T00:00:00Z&to=2025-10-14T00:00:00Z",
+ ];
+ for uri in uris {
+ let (status, _, body) = send(&app, Method::GET, uri, None).await;
+ assert_eq!(status, StatusCode::BAD_REQUEST, "{uri}");
+ assert!(body["error"].is_string(), "{uri}: {body}");
+ }
+ let (status, _, _) = send(&app, Method::POST, "/measurements/export", None).await;
+ assert_eq!(status, StatusCode::METHOD_NOT_ALLOWED);
+}