tests: fix flaky tests
This commit is contained in:
@@ -25,3 +25,4 @@ serde_json = "1"
|
|||||||
dotenvy = "0.15"
|
dotenvy = "0.15"
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
uuid = { version = "1", features = ["serde", "v4"] }
|
uuid = { version = "1", features = ["serde", "v4"] }
|
||||||
|
serial_test = "3"
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
use onspring::OnspringClient;
|
use onspring::OnspringClient;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::future::Future;
|
||||||
use std::sync::Once;
|
use std::sync::Once;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
static INIT: Once = Once::new();
|
static INIT: Once = Once::new();
|
||||||
|
|
||||||
@@ -64,3 +66,25 @@ pub async fn add_record() -> i32 {
|
|||||||
let response = client.save_record(request).await.unwrap();
|
let response = client.save_record(request).await.unwrap();
|
||||||
response.id
|
response.id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn retry<F, Fut, T>(max_attempts: u32, delay: Duration, f: F) -> T
|
||||||
|
where
|
||||||
|
F: Fn() -> Fut,
|
||||||
|
Fut: Future<Output = Result<T, Box<dyn std::error::Error>>>,
|
||||||
|
{
|
||||||
|
let mut last_err = None;
|
||||||
|
for attempt in 0..max_attempts {
|
||||||
|
if attempt > 0 {
|
||||||
|
tokio::time::sleep(delay).await;
|
||||||
|
}
|
||||||
|
match f().await {
|
||||||
|
Ok(val) => return val,
|
||||||
|
Err(e) => last_err = Some(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic!(
|
||||||
|
"retry failed after {} attempts: {}",
|
||||||
|
max_attempts,
|
||||||
|
last_err.unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
+63
-23
@@ -1,10 +1,14 @@
|
|||||||
mod integration_common;
|
mod integration_common;
|
||||||
|
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use integration_common::*;
|
use integration_common::*;
|
||||||
use onspring::SaveListItemRequest;
|
use onspring::SaveListItemRequest;
|
||||||
|
use serial_test::serial;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
|
#[serial]
|
||||||
async fn save_list_item_should_add_item() {
|
async fn save_list_item_should_add_item() {
|
||||||
let client = build_client();
|
let client = build_client();
|
||||||
let list_id = required_env_i32("TEST_LIST_ID");
|
let list_id = required_env_i32("TEST_LIST_ID");
|
||||||
@@ -20,17 +24,25 @@ async fn save_list_item_should_add_item() {
|
|||||||
let response = client.save_list_item(list_id, request).await.unwrap();
|
let response = client.save_list_item(list_id, request).await.unwrap();
|
||||||
let item_id = response.id;
|
let item_id = response.id;
|
||||||
|
|
||||||
// Cleanup
|
retry(5, Duration::from_secs(1), || {
|
||||||
let _ = client.delete_list_item(list_id, item_id).await;
|
let client = build_client();
|
||||||
|
async move {
|
||||||
|
client
|
||||||
|
.delete_list_item(list_id, item_id)
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.into())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
|
#[serial]
|
||||||
async fn save_list_item_should_update_item() {
|
async fn save_list_item_should_update_item() {
|
||||||
let client = build_client();
|
let client = build_client();
|
||||||
let list_id = required_env_i32("TEST_LIST_ID");
|
let list_id = required_env_i32("TEST_LIST_ID");
|
||||||
|
|
||||||
// Create
|
|
||||||
let name = format!("added_list_value_{}", chrono::Utc::now().timestamp_millis());
|
let name = format!("added_list_value_{}", chrono::Utc::now().timestamp_millis());
|
||||||
let request = SaveListItemRequest {
|
let request = SaveListItemRequest {
|
||||||
id: None,
|
id: None,
|
||||||
@@ -40,28 +52,42 @@ async fn save_list_item_should_update_item() {
|
|||||||
};
|
};
|
||||||
let created = client.save_list_item(list_id, request).await.unwrap();
|
let created = client.save_list_item(list_id, request).await.unwrap();
|
||||||
|
|
||||||
// Update
|
let updated = retry(5, Duration::from_secs(1), || {
|
||||||
let name = format!(
|
let client = build_client();
|
||||||
"updated_list_value_{}",
|
let name = format!(
|
||||||
chrono::Utc::now().timestamp_millis()
|
"updated_list_value_{}",
|
||||||
);
|
chrono::Utc::now().timestamp_millis()
|
||||||
let update_request = SaveListItemRequest {
|
);
|
||||||
id: Some(created.id),
|
async move {
|
||||||
name,
|
let update_request = SaveListItemRequest {
|
||||||
numeric_value: Some(1.0),
|
id: Some(created.id),
|
||||||
color: Some("#000000".to_string()),
|
name,
|
||||||
};
|
numeric_value: Some(1.0),
|
||||||
let _response = client
|
color: Some("#000000".to_string()),
|
||||||
.save_list_item(list_id, update_request)
|
};
|
||||||
.await
|
client
|
||||||
.unwrap();
|
.save_list_item(list_id, update_request)
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.into())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
// Cleanup
|
retry(5, Duration::from_secs(1), || {
|
||||||
let _ = client.delete_list_item(list_id, created.id).await;
|
let client = build_client();
|
||||||
|
async move {
|
||||||
|
client
|
||||||
|
.delete_list_item(list_id, updated.id)
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.into())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
|
#[serial]
|
||||||
async fn save_list_item_should_fail_with_invalid_api_key() {
|
async fn save_list_item_should_fail_with_invalid_api_key() {
|
||||||
let client = build_client_with_key("invalid");
|
let client = build_client_with_key("invalid");
|
||||||
let request = SaveListItemRequest {
|
let request = SaveListItemRequest {
|
||||||
@@ -76,6 +102,7 @@ async fn save_list_item_should_fail_with_invalid_api_key() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
|
#[serial]
|
||||||
async fn save_list_item_should_fail_when_list_not_found() {
|
async fn save_list_item_should_fail_when_list_not_found() {
|
||||||
let client = build_client();
|
let client = build_client();
|
||||||
let request = SaveListItemRequest {
|
let request = SaveListItemRequest {
|
||||||
@@ -90,6 +117,7 @@ async fn save_list_item_should_fail_when_list_not_found() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
|
#[serial]
|
||||||
async fn save_list_item_should_fail_when_item_not_found() {
|
async fn save_list_item_should_fail_when_item_not_found() {
|
||||||
let client = build_client();
|
let client = build_client();
|
||||||
let list_id = required_env_i32("TEST_LIST_ID");
|
let list_id = required_env_i32("TEST_LIST_ID");
|
||||||
@@ -107,11 +135,11 @@ async fn save_list_item_should_fail_when_item_not_found() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
|
#[serial]
|
||||||
async fn delete_list_item_should_succeed() {
|
async fn delete_list_item_should_succeed() {
|
||||||
let client = build_client();
|
let client = build_client();
|
||||||
let list_id = required_env_i32("TEST_LIST_ID");
|
let list_id = required_env_i32("TEST_LIST_ID");
|
||||||
|
|
||||||
// Create first
|
|
||||||
let name = format!("to_delete_{}", chrono::Utc::now().timestamp_millis());
|
let name = format!("to_delete_{}", chrono::Utc::now().timestamp_millis());
|
||||||
let request = SaveListItemRequest {
|
let request = SaveListItemRequest {
|
||||||
id: None,
|
id: None,
|
||||||
@@ -121,12 +149,21 @@ async fn delete_list_item_should_succeed() {
|
|||||||
};
|
};
|
||||||
let created = client.save_list_item(list_id, request).await.unwrap();
|
let created = client.save_list_item(list_id, request).await.unwrap();
|
||||||
|
|
||||||
// Delete
|
retry(5, Duration::from_secs(1), || {
|
||||||
client.delete_list_item(list_id, created.id).await.unwrap();
|
let client = build_client();
|
||||||
|
async move {
|
||||||
|
client
|
||||||
|
.delete_list_item(list_id, created.id)
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.into())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
|
#[serial]
|
||||||
async fn delete_list_item_should_fail_with_invalid_api_key() {
|
async fn delete_list_item_should_fail_with_invalid_api_key() {
|
||||||
let client = build_client_with_key("invalid");
|
let client = build_client_with_key("invalid");
|
||||||
let fake_uuid = uuid::Uuid::parse_str("3fa85f64-5717-4562-b3fc-2c963f66afa6").unwrap();
|
let fake_uuid = uuid::Uuid::parse_str("3fa85f64-5717-4562-b3fc-2c963f66afa6").unwrap();
|
||||||
@@ -136,6 +173,7 @@ async fn delete_list_item_should_fail_with_invalid_api_key() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
|
#[serial]
|
||||||
async fn delete_list_item_should_fail_when_no_access() {
|
async fn delete_list_item_should_fail_when_no_access() {
|
||||||
let client = build_client();
|
let client = build_client();
|
||||||
let list_id = required_env_i32("TEST_LIST_ID_NO_ACCESS");
|
let list_id = required_env_i32("TEST_LIST_ID_NO_ACCESS");
|
||||||
@@ -148,6 +186,7 @@ async fn delete_list_item_should_fail_when_no_access() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
|
#[serial]
|
||||||
async fn delete_list_item_should_fail_when_list_not_found() {
|
async fn delete_list_item_should_fail_when_list_not_found() {
|
||||||
let client = build_client();
|
let client = build_client();
|
||||||
let fake_uuid = uuid::Uuid::parse_str("3fa85f64-5717-4562-b3fc-2c963f66afa6").unwrap();
|
let fake_uuid = uuid::Uuid::parse_str("3fa85f64-5717-4562-b3fc-2c963f66afa6").unwrap();
|
||||||
@@ -157,6 +196,7 @@ async fn delete_list_item_should_fail_when_list_not_found() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
|
#[serial]
|
||||||
async fn delete_list_item_should_fail_when_item_not_found() {
|
async fn delete_list_item_should_fail_when_item_not_found() {
|
||||||
let client = build_client();
|
let client = build_client();
|
||||||
let list_id = required_env_i32("TEST_LIST_ID");
|
let list_id = required_env_i32("TEST_LIST_ID");
|
||||||
|
|||||||
@@ -4,15 +4,14 @@ use integration_common::{build_client, build_client_with_key};
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
async fn ping_should_succeed() {
|
async fn ping_should_succeed_with_api_key() {
|
||||||
let client = build_client();
|
let client = build_client();
|
||||||
client.ping().await.unwrap();
|
client.ping().await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
async fn ping_should_fail_with_invalid_api_key() {
|
async fn ping_should_succeed_without_api_key() {
|
||||||
let client = build_client_with_key("invalid");
|
let client = build_client_with_key("");
|
||||||
let result = client.ping().await;
|
client.ping().await.unwrap();
|
||||||
assert!(result.is_err());
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user