mirror of
https://github.com/lennart-k/rustical.git
synced 2026-01-30 04:38:19 +00:00
Add more abstract integration test
This commit is contained in:
3
Cargo.lock
generated
3
Cargo.lock
generated
@@ -3319,6 +3319,7 @@ dependencies = [
|
|||||||
"caldata",
|
"caldata",
|
||||||
"clap",
|
"clap",
|
||||||
"figment",
|
"figment",
|
||||||
|
"futures-util",
|
||||||
"headers",
|
"headers",
|
||||||
"http",
|
"http",
|
||||||
"insta",
|
"insta",
|
||||||
@@ -3344,6 +3345,7 @@ dependencies = [
|
|||||||
"similar-asserts",
|
"similar-asserts",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
"toml 0.9.11+spec-1.1.0",
|
"toml 0.9.11+spec-1.1.0",
|
||||||
"tower",
|
"tower",
|
||||||
"tower-http",
|
"tower-http",
|
||||||
@@ -4471,6 +4473,7 @@ dependencies = [
|
|||||||
"bytes",
|
"bytes",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
|
"futures-util",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ tokio = { version = "1.48", features = [
|
|||||||
"rt-multi-thread",
|
"rt-multi-thread",
|
||||||
"full",
|
"full",
|
||||||
] }
|
] }
|
||||||
|
tokio-util = { version = "0.7", features = ["rt"] }
|
||||||
url = "2.5"
|
url = "2.5"
|
||||||
base64 = "0.22"
|
base64 = "0.22"
|
||||||
thiserror = "2.0"
|
thiserror = "2.0"
|
||||||
@@ -166,6 +167,7 @@ caldata.workspace = true
|
|||||||
toml.workspace = true
|
toml.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
tokio.workspace = true
|
tokio.workspace = true
|
||||||
|
tokio-util.workspace = true
|
||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
clap.workspace = true
|
clap.workspace = true
|
||||||
@@ -204,3 +206,4 @@ tower-http.workspace = true
|
|||||||
axum-extra.workspace = true
|
axum-extra.workspace = true
|
||||||
headers.workspace = true
|
headers.workspace = true
|
||||||
http.workspace = true
|
http.workspace = true
|
||||||
|
futures-util.workspace = true
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
use rustical::{
|
||||||
|
Args, cmd_default,
|
||||||
|
config::{Config, DataStoreConfig, HttpConfig, SqliteDataStoreConfig},
|
||||||
|
};
|
||||||
|
use std::{
|
||||||
|
net::{Ipv4Addr, SocketAddrV4, TcpListener},
|
||||||
|
sync::Arc,
|
||||||
|
thread::{self, JoinHandle},
|
||||||
|
};
|
||||||
|
use tokio::sync::Notify;
|
||||||
|
use tokio_util::sync::CancellationToken;
|
||||||
|
|
||||||
|
pub fn find_free_port() -> Option<u16> {
|
||||||
|
let mut port = 15000;
|
||||||
|
// Frees the socket on drop such that this function returns a free port
|
||||||
|
while TcpListener::bind(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, port)).is_err() {
|
||||||
|
port += 1;
|
||||||
|
|
||||||
|
if port >= 16000 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(port)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rustical_process() -> (CancellationToken, u16, JoinHandle<()>, Arc<Notify>) {
|
||||||
|
let port = find_free_port().unwrap();
|
||||||
|
let token = CancellationToken::new();
|
||||||
|
let cloned_token = token.clone();
|
||||||
|
let start_notify = Arc::new(Notify::new());
|
||||||
|
let cloned_start_notify = start_notify.clone();
|
||||||
|
|
||||||
|
let main_process = thread::spawn(move || {
|
||||||
|
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||||
|
let fut = async {
|
||||||
|
cmd_default(
|
||||||
|
Args {
|
||||||
|
config_file: "asldajldakjsdkj".to_owned(),
|
||||||
|
no_migrations: false,
|
||||||
|
command: None,
|
||||||
|
},
|
||||||
|
Config {
|
||||||
|
data_store: DataStoreConfig::Sqlite(SqliteDataStoreConfig {
|
||||||
|
db_url: ":memory:".to_owned(),
|
||||||
|
run_repairs: true,
|
||||||
|
skip_broken: false,
|
||||||
|
}),
|
||||||
|
http: HttpConfig {
|
||||||
|
host: "127.0.0.1".to_owned(),
|
||||||
|
port,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
frontend: Default::default(),
|
||||||
|
oidc: None,
|
||||||
|
tracing: Default::default(),
|
||||||
|
dav_push: Default::default(),
|
||||||
|
nextcloud_login: Default::default(),
|
||||||
|
caldav: Default::default(),
|
||||||
|
},
|
||||||
|
Some(cloned_start_notify),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
};
|
||||||
|
rt.block_on(async {
|
||||||
|
tokio::select! {
|
||||||
|
_ = cloned_token.cancelled() => {},
|
||||||
|
_ = fut => {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
(token, port, main_process, start_notify)
|
||||||
|
}
|
||||||
|
|||||||
44
tests/http_integration.rs
Normal file
44
tests/http_integration.rs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
// This integration test checks whether the HTTP server works by actually running rustical in a new
|
||||||
|
// thread.
|
||||||
|
use common::rustical_process;
|
||||||
|
use http::StatusCode;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
mod common;
|
||||||
|
|
||||||
|
pub async fn test_runner<O, F>(inner: F)
|
||||||
|
where
|
||||||
|
O: IntoFuture<Output = ()>,
|
||||||
|
// <O as IntoFuture>::IntoFuture: UnwindSafe,
|
||||||
|
F: FnOnce(String) -> O,
|
||||||
|
{
|
||||||
|
// Start RustiCal process
|
||||||
|
let (token, port, main_process, start_notify) = rustical_process();
|
||||||
|
let origin = format!("http://localhost:{port}");
|
||||||
|
|
||||||
|
// Wait for RustiCal server to listen
|
||||||
|
tokio::time::timeout(Duration::new(2, 0), start_notify.notified())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// We use catch_unwind to make sure we'll always correctly stop RustiCal
|
||||||
|
// Otherwise, our process would just run indefinitely
|
||||||
|
inner(origin).into_future().await;
|
||||||
|
|
||||||
|
// Signal RustiCal to stop
|
||||||
|
token.cancel();
|
||||||
|
main_process.join().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_ping() {
|
||||||
|
test_runner(async |origin| {
|
||||||
|
let resp = reqwest::get(origin.clone() + "/ping").await.unwrap();
|
||||||
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
|
||||||
|
// Ensure that path normalisation works as intended
|
||||||
|
let resp = reqwest::get(origin + "/ping/").await.unwrap();
|
||||||
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
@@ -1 +0,0 @@
|
|||||||
mod integration_tests;
|
|
||||||
3
tests/run_integration_tests.rs
Normal file
3
tests/run_integration_tests.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
// This file is just an entrypoint for integration_tests
|
||||||
|
// since the test runner only looks for files to run.
|
||||||
|
mod integration_tests;
|
||||||
Reference in New Issue
Block a user