mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-07-07 20:06:36 +02:00
add tests for API methods
This commit is contained in:
parent
3fb2327a5b
commit
7472749fee
8 changed files with 299 additions and 14 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -2035,6 +2035,7 @@ dependencies = [
|
|||
name = "sync-server"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"actix-rt",
|
||||
"actix-web",
|
||||
"failure",
|
||||
"futures",
|
||||
|
|
|
@ -9,5 +9,8 @@ edition = "2018"
|
|||
[dependencies]
|
||||
actix-web = "3.3.0"
|
||||
failure = "0.1.8"
|
||||
futures = "0.3.8"
|
||||
taskchampion = { path = "../taskchampion" }
|
||||
futures = "0.3.8"
|
||||
|
||||
[dev-dependencies]
|
||||
actix-rt = "1.1.1"
|
||||
|
|
|
@ -43,6 +43,10 @@ pub(crate) async fn service(
|
|||
body.extend_from_slice(&chunk);
|
||||
}
|
||||
|
||||
if body.is_empty() {
|
||||
return Err(error::ErrorBadRequest("Empty body"));
|
||||
}
|
||||
|
||||
let result = data
|
||||
.add_version(client_id, parent_version_id, body.to_vec())
|
||||
.map_err(|e| error::InternalError::new(e, StatusCode::INTERNAL_SERVER_ERROR))?;
|
||||
|
@ -55,3 +59,122 @@ pub(crate) async fn service(
|
|||
.body(""),
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::api::ServerState;
|
||||
use crate::app_scope;
|
||||
use crate::server::SyncServer;
|
||||
use crate::test::TestServer;
|
||||
use actix_web::{test, App};
|
||||
use taskchampion::Uuid;
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_success() {
|
||||
let client_id = Uuid::new_v4();
|
||||
let version_id = Uuid::new_v4();
|
||||
let parent_version_id = Uuid::new_v4();
|
||||
let server_box: Box<dyn SyncServer> = Box::new(TestServer {
|
||||
expected_client_id: client_id,
|
||||
av_expected_parent_version_id: parent_version_id,
|
||||
av_expected_history_segment: b"abcd".to_vec(),
|
||||
av_result: Some(AddVersionResult::Ok(version_id)),
|
||||
..Default::default()
|
||||
});
|
||||
let server_state = ServerState::new(server_box);
|
||||
let mut app = test::init_service(App::new().service(app_scope(server_state))).await;
|
||||
|
||||
let uri = format!("/client/{}/add-version/{}", client_id, parent_version_id);
|
||||
let req = test::TestRequest::post()
|
||||
.uri(&uri)
|
||||
.header(
|
||||
"Content-Type",
|
||||
"application/vnd.taskchampion.history-segment",
|
||||
)
|
||||
.set_payload(b"abcd".to_vec())
|
||||
.to_request();
|
||||
let resp = test::call_service(&mut app, req).await;
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
resp.headers().get("X-Version-Id").unwrap(),
|
||||
&version_id.to_string()
|
||||
);
|
||||
assert_eq!(resp.headers().get("X-Parent-Version-Id"), None);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_conflict() {
|
||||
let client_id = Uuid::new_v4();
|
||||
let version_id = Uuid::new_v4();
|
||||
let parent_version_id = Uuid::new_v4();
|
||||
let server_box: Box<dyn SyncServer> = Box::new(TestServer {
|
||||
expected_client_id: client_id,
|
||||
av_expected_parent_version_id: parent_version_id,
|
||||
av_expected_history_segment: b"abcd".to_vec(),
|
||||
av_result: Some(AddVersionResult::ExpectedParentVersion(version_id)),
|
||||
..Default::default()
|
||||
});
|
||||
let server_state = ServerState::new(server_box);
|
||||
let mut app = test::init_service(App::new().service(app_scope(server_state))).await;
|
||||
|
||||
let uri = format!("/client/{}/add-version/{}", client_id, parent_version_id);
|
||||
let req = test::TestRequest::post()
|
||||
.uri(&uri)
|
||||
.header(
|
||||
"Content-Type",
|
||||
"application/vnd.taskchampion.history-segment",
|
||||
)
|
||||
.set_payload(b"abcd".to_vec())
|
||||
.to_request();
|
||||
let resp = test::call_service(&mut app, req).await;
|
||||
assert_eq!(resp.status(), StatusCode::CONFLICT);
|
||||
assert_eq!(resp.headers().get("X-Version-Id"), None);
|
||||
assert_eq!(
|
||||
resp.headers().get("X-Parent-Version-Id").unwrap(),
|
||||
&version_id.to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_bad_content_type() {
|
||||
let client_id = Uuid::new_v4();
|
||||
let parent_version_id = Uuid::new_v4();
|
||||
let server_box: Box<dyn SyncServer> = Box::new(TestServer {
|
||||
..Default::default()
|
||||
});
|
||||
let server_state = ServerState::new(server_box);
|
||||
let mut app = test::init_service(App::new().service(app_scope(server_state))).await;
|
||||
|
||||
let uri = format!("/client/{}/add-version/{}", client_id, parent_version_id);
|
||||
let req = test::TestRequest::post()
|
||||
.uri(&uri)
|
||||
.header("Content-Type", "not/correct")
|
||||
.set_payload(b"abcd".to_vec())
|
||||
.to_request();
|
||||
let resp = test::call_service(&mut app, req).await;
|
||||
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_empty_body() {
|
||||
let client_id = Uuid::new_v4();
|
||||
let parent_version_id = Uuid::new_v4();
|
||||
let server_box: Box<dyn SyncServer> = Box::new(TestServer {
|
||||
..Default::default()
|
||||
});
|
||||
let server_state = ServerState::new(server_box);
|
||||
let mut app = test::init_service(App::new().service(app_scope(server_state))).await;
|
||||
|
||||
let uri = format!("/client/{}/add-version/{}", client_id, parent_version_id);
|
||||
let req = test::TestRequest::post()
|
||||
.uri(&uri)
|
||||
.header(
|
||||
"Content-Type",
|
||||
"application/vnd.taskchampion.history-segment",
|
||||
)
|
||||
.to_request();
|
||||
let resp = test::call_service(&mut app, req).await;
|
||||
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,3 +33,81 @@ pub(crate) async fn service(
|
|||
Err(error::ErrorNotFound("no such version"))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::api::ServerState;
|
||||
use crate::app_scope;
|
||||
use crate::server::{GetVersionResult, SyncServer};
|
||||
use crate::test::TestServer;
|
||||
use actix_web::{test, App};
|
||||
use taskchampion::Uuid;
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_success() {
|
||||
let client_id = Uuid::new_v4();
|
||||
let version_id = Uuid::new_v4();
|
||||
let parent_version_id = Uuid::new_v4();
|
||||
let server_box: Box<dyn SyncServer> = Box::new(TestServer {
|
||||
expected_client_id: client_id,
|
||||
gcv_expected_parent_version_id: parent_version_id,
|
||||
gcv_result: Some(GetVersionResult {
|
||||
version_id: version_id,
|
||||
parent_version_id: parent_version_id,
|
||||
history_segment: b"abcd".to_vec(),
|
||||
}),
|
||||
..Default::default()
|
||||
});
|
||||
let server_state = ServerState::new(server_box);
|
||||
let mut app = test::init_service(App::new().service(app_scope(server_state))).await;
|
||||
|
||||
let uri = format!(
|
||||
"/client/{}/get-child-version/{}",
|
||||
client_id, parent_version_id
|
||||
);
|
||||
let req = test::TestRequest::get().uri(&uri).to_request();
|
||||
let mut resp = test::call_service(&mut app, req).await;
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
resp.headers().get("X-Version-Id").unwrap(),
|
||||
&version_id.to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
resp.headers().get("X-Parent-Version-Id").unwrap(),
|
||||
&parent_version_id.to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
resp.headers().get("Content-Type").unwrap(),
|
||||
&"application/vnd.taskchampion.history-segment".to_string()
|
||||
);
|
||||
|
||||
use futures::StreamExt;
|
||||
let (bytes, _) = resp.take_body().into_future().await;
|
||||
assert_eq!(bytes.unwrap().unwrap().as_ref(), b"abcd");
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_not_found() {
|
||||
let client_id = Uuid::new_v4();
|
||||
let parent_version_id = Uuid::new_v4();
|
||||
let server_box: Box<dyn SyncServer> = Box::new(TestServer {
|
||||
expected_client_id: client_id,
|
||||
gcv_expected_parent_version_id: parent_version_id,
|
||||
gcv_result: None,
|
||||
..Default::default()
|
||||
});
|
||||
let server_state = ServerState::new(server_box);
|
||||
let mut app = test::init_service(App::new().service(app_scope(server_state))).await;
|
||||
|
||||
let uri = format!(
|
||||
"/client/{}/get-child-version/{}",
|
||||
client_id, parent_version_id
|
||||
);
|
||||
let req = test::TestRequest::get().uri(&uri).to_request();
|
||||
let resp = test::call_service(&mut app, req).await;
|
||||
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
||||
assert_eq!(resp.headers().get("X-Version-Id"), None);
|
||||
assert_eq!(resp.headers().get("X-Parent-Version-Id"), None);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use crate::server::SyncServer;
|
||||
use actix_web::{web, Scope};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub(crate) mod add_version;
|
||||
pub(crate) mod get_child_version;
|
||||
mod add_version;
|
||||
mod get_child_version;
|
||||
|
||||
/// The content-type for history segments (opaque blobs of bytes)
|
||||
pub(crate) const HISTORY_SEGMENT_CONTENT_TYPE: &str =
|
||||
|
@ -16,3 +17,9 @@ pub(crate) const PARENT_VERSION_ID_HEADER: &str = "X-Parent-Version-Id";
|
|||
|
||||
/// The type containing a reference to the SyncServer object in the Actix state.
|
||||
pub(crate) type ServerState = Arc<Box<dyn SyncServer>>;
|
||||
|
||||
pub(crate) fn api_scope() -> Scope {
|
||||
web::scope("")
|
||||
.service(get_child_version::service)
|
||||
.service(add_version::service)
|
||||
}
|
||||
|
|
|
@ -1,23 +1,36 @@
|
|||
use actix_web::{App, HttpServer};
|
||||
use api::ServerState;
|
||||
use actix_web::{get, web, App, HttpServer, Responder, Scope};
|
||||
use api::{api_scope, ServerState};
|
||||
use server::{InMemorySyncServer, SyncServer};
|
||||
|
||||
mod api;
|
||||
mod server;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
||||
// TODO: use hawk to sign requests
|
||||
|
||||
#[get("/")]
|
||||
async fn index() -> impl Responder {
|
||||
// TODO: add version here
|
||||
"TaskChampion sync server"
|
||||
}
|
||||
|
||||
/// Return a scope defining the URL rules for this server, with access to
|
||||
/// the given ServerState.
|
||||
pub(crate) fn app_scope(server_state: ServerState) -> Scope {
|
||||
web::scope("")
|
||||
.data(server_state)
|
||||
.service(index)
|
||||
.service(api_scope())
|
||||
}
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
let server_box: Box<dyn SyncServer> = Box::new(InMemorySyncServer::new());
|
||||
let server_state = ServerState::new(server_box);
|
||||
|
||||
HttpServer::new(move || {
|
||||
App::new()
|
||||
.data(server_state.clone())
|
||||
.service(api::get_child_version::service)
|
||||
.service(api::add_version::service)
|
||||
})
|
||||
HttpServer::new(move || App::new().service(app_scope(server_state.clone())))
|
||||
.bind("127.0.0.1:8080")?
|
||||
.run()
|
||||
.await
|
||||
|
|
|
@ -13,6 +13,7 @@ pub(crate) type ClientId = Uuid;
|
|||
pub(crate) type VersionId = Uuid;
|
||||
|
||||
/// Response to get_child_version
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct GetVersionResult {
|
||||
pub(crate) version_id: Uuid,
|
||||
pub(crate) parent_version_id: Uuid,
|
||||
|
@ -20,12 +21,14 @@ pub(crate) struct GetVersionResult {
|
|||
}
|
||||
|
||||
/// Response to add_version
|
||||
#[derive(Clone)]
|
||||
pub(crate) enum AddVersionResult {
|
||||
/// OK, version added with the given ID
|
||||
Ok(VersionId),
|
||||
/// Rejected; expected a version with the given parent version
|
||||
ExpectedParentVersion(VersionId),
|
||||
}
|
||||
|
||||
pub(crate) trait SyncServer: Sync + Send {
|
||||
fn get_child_version(
|
||||
&self,
|
||||
|
|
57
sync-server/src/test.rs
Normal file
57
sync-server/src/test.rs
Normal file
|
@ -0,0 +1,57 @@
|
|||
use crate::api::ServerState;
|
||||
use crate::app_scope;
|
||||
use crate::server::{
|
||||
AddVersionResult, ClientId, GetVersionResult, HistorySegment, SyncServer, VersionId,
|
||||
};
|
||||
use actix_web::{test, App};
|
||||
use failure::Fallible;
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct TestServer {
|
||||
/// test server will panic if this is not given
|
||||
pub expected_client_id: ClientId,
|
||||
|
||||
pub gcv_expected_parent_version_id: VersionId,
|
||||
pub gcv_result: Option<GetVersionResult>,
|
||||
|
||||
pub av_expected_parent_version_id: VersionId,
|
||||
pub av_expected_history_segment: HistorySegment,
|
||||
pub av_result: Option<AddVersionResult>,
|
||||
}
|
||||
|
||||
impl SyncServer for TestServer {
|
||||
fn get_child_version(
|
||||
&self,
|
||||
client_id: ClientId,
|
||||
parent_version_id: VersionId,
|
||||
) -> Fallible<Option<GetVersionResult>> {
|
||||
assert_eq!(client_id, self.expected_client_id);
|
||||
assert_eq!(parent_version_id, self.gcv_expected_parent_version_id);
|
||||
Ok(self.gcv_result.clone())
|
||||
}
|
||||
|
||||
fn add_version(
|
||||
&self,
|
||||
client_id: ClientId,
|
||||
parent_version_id: VersionId,
|
||||
history_segment: HistorySegment,
|
||||
) -> Fallible<AddVersionResult> {
|
||||
assert_eq!(client_id, self.expected_client_id);
|
||||
assert_eq!(parent_version_id, self.av_expected_parent_version_id);
|
||||
assert_eq!(history_segment, self.av_expected_history_segment);
|
||||
Ok(self.av_result.clone().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_index_get() {
|
||||
let server_box: Box<dyn SyncServer> = Box::new(TestServer {
|
||||
..Default::default()
|
||||
});
|
||||
let server_state = ServerState::new(server_box);
|
||||
let mut app = test::init_service(App::new().service(app_scope(server_state))).await;
|
||||
|
||||
let req = test::TestRequest::get().uri("/").to_request();
|
||||
let resp = test::call_service(&mut app, req).await;
|
||||
assert!(resp.status().is_success());
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue