integration test sharing between backends

This commit is contained in:
Antoine Gersant 2020-01-16 00:34:47 -08:00
parent a83e1af69b
commit 61c221a2d2
17 changed files with 221 additions and 281 deletions

1
Cargo.lock generated
View file

@ -2071,6 +2071,7 @@ dependencies = [
"anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
"ape 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"app_dirs 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"async-trait 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
"base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"diesel 1.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"diesel_migrations 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",

View file

@ -8,17 +8,18 @@ edition = "2018"
default = ["service-actix"]
ui = []
profile-index = ["flame", "flamer"]
service-actix = ["actix-files", "actix-http", "actix-rt", "actix-web"]
service-actix = ["actix-files", "actix-http", "actix-web"]
service-rocket = ["rocket", "rocket_contrib"]
[dependencies]
actix-files = { version = "0.2", optional = true }
actix-http = { version = "1.0", optional = true }
actix-web = { version = "2.0", optional = true }
actix-rt = { version = "1.0", optional = true }
actix-rt = { version = "1.0"}
anyhow = "1.0"
ape = "0.2.0"
app_dirs = "1.1.1"
async-trait = "0.1.22"
base64 = "0.11.0"
diesel = { version = "1.4", features = ["sqlite", "r2d2"] }
diesel_migrations = { version = "1.4", features = ["sqlite"] }

View file

@ -1,5 +1,5 @@
#![recursion_limit = "256"]
#![feature(proc_macro_hygiene, decl_macro)]
#![feature(proc_macro_hygiene, decl_macro, type_alias_impl_trait)]
#[macro_use]
extern crate diesel;

View file

@ -2,7 +2,7 @@ use actix_http::ResponseBuilder;
use actix_web::{error, get, http::header, http::StatusCode, put, web, HttpResponse};
use anyhow::*;
use crate::config::{self, Config, Preferences};
use crate::config::{self, Config};
use crate::db::DB;
use crate::service::constants::*;
use crate::service::dto;
@ -46,7 +46,7 @@ pub async fn get_initial_setup(db: web::Data<DB>) -> Result<HttpResponse, APIErr
#[put("/settings")]
pub async fn put_settings(
db: web::Data<DB>,
// _admin_rights: AdminRights, // TODO
// _admin_rights: AdminRights, // TODO.important
config: web::Json<Config>,
) -> Result<HttpResponse, APIError> {
web::block(move || config::amend(&db, &config))

View file

@ -8,7 +8,7 @@ pub mod server;
mod api;
#[cfg(test)]
mod tests;
pub mod test;
fn configure_app(
cfg: &mut web::ServiceConfig,

84
src/service/actix/test.rs Normal file
View file

@ -0,0 +1,84 @@
use actix_http::{Error, Request};
use actix_web::dev::{Body, ResponseBody, Service, ServiceResponse};
use actix_web::test::TestRequest;
use actix_web::{test, App};
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::fs;
use std::path::PathBuf;
use crate::db::DB;
fn configure_test_app(cfg: &mut actix_web::web::ServiceConfig, db_name: &str) {
let web_url = "/";
let web_dir_path = PathBuf::from("web");
let swagger_url = "swagger";
let mut swagger_dir_path = PathBuf::from("docs");
swagger_dir_path.push("swagger");
let mut db_path = PathBuf::new();
db_path.push("test");
db_path.push(format!("{}.sqlite", db_name));
if db_path.exists() {
fs::remove_file(&db_path).unwrap();
}
let db = DB::new(&db_path).unwrap();
super::configure_app(
cfg,
web_url,
web_dir_path.as_path(),
swagger_url,
swagger_dir_path.as_path(),
&db,
);
}
pub type ServiceType = impl Service<Request = Request, Response = ServiceResponse, Error = Error>;
pub async fn make_service(test_name: &str) -> ServiceType {
let app = App::new().configure(|cfg| configure_test_app(cfg, test_name));
let service = test::init_service(app).await;
service
}
pub async fn get(service: &mut ServiceType, url: &str) {
let req = TestRequest::get().uri(url).to_request();
let resp = service.call(req).await.unwrap();
assert!(resp.status().is_success());
}
pub async fn get_json<T: DeserializeOwned>(service: &mut ServiceType, url: &str) -> T {
let req = TestRequest::get().uri(url).to_request();
let resp = service.call(req).await.unwrap();
assert!(resp.status().is_success());
let body = resp.response().body().as_u8();
let response_json: T = serde_json::from_slice(body).unwrap();
response_json
}
pub async fn put_json<T: Serialize>(service: &mut ServiceType, url: &str, payload: &T) {
let req = TestRequest::put().uri(url).set_json(payload).to_request();
let resp = service.call(req).await.unwrap();
assert!(resp.status().is_success());
}
trait BodyToBytes {
fn as_u8(&self) -> &[u8];
}
impl BodyToBytes for ResponseBody<Body> {
fn as_u8(&self) -> &[u8] {
match self {
ResponseBody::Body(ref b) => match b {
Body::Bytes(ref by) => by.as_ref(),
_ => panic!(),
},
ResponseBody::Other(ref b) => match b {
Body::Bytes(ref by) => by.as_ref(),
_ => panic!(),
},
}
}
}

View file

@ -1,117 +0,0 @@
use actix_http::Request;
use actix_web::dev::*;
use actix_web::test::TestRequest;
use actix_web::{test, App};
use function_name::named;
use super::configure_test_app;
use crate::config;
use crate::service::dto;
use crate::vfs;
const TEST_USERNAME: &str = "test_user";
const TEST_PASSWORD: &str = "test_password";
const TEST_MOUNT_NAME: &str = "collection";
const TEST_MOUNT_SOURCE: &str = "test/collection";
trait BodyTest {
fn as_u8(&self) -> &[u8];
}
impl BodyTest for ResponseBody<Body> {
fn as_u8(&self) -> &[u8] {
match self {
ResponseBody::Body(ref b) => match b {
Body::Bytes(ref by) => by.as_ref(),
_ => panic!(),
},
ResponseBody::Other(ref b) => match b {
Body::Bytes(ref by) => by.as_ref(),
_ => panic!(),
},
}
}
}
fn initial_setup() -> Request {
let configuration = config::Config {
album_art_pattern: None,
prefix_url: None,
reindex_every_n_seconds: None,
ydns: None,
users: Some(vec![config::ConfigUser {
name: TEST_USERNAME.into(),
password: TEST_PASSWORD.into(),
admin: true,
}]),
mount_dirs: Some(vec![vfs::MountPoint {
name: TEST_MOUNT_NAME.into(),
source: TEST_MOUNT_SOURCE.into(),
}]),
};
TestRequest::put()
.uri("/api/settings")
.set_json(&configuration)
.to_request()
}
#[named]
#[actix_rt::test]
async fn test_version() {
let app = App::new().configure(|cfg| configure_test_app(cfg, function_name!()));
let mut service = test::init_service(app).await;
let req = TestRequest::get().uri("/api/version").to_request();
let resp = service.call(req).await.unwrap();
assert!(resp.status().is_success());
let body = resp.response().body().as_u8();
let response_json: dto::Version = serde_json::from_slice(body).unwrap();
assert_eq!(response_json, dto::Version { major: 4, minor: 0 });
}
#[named]
#[actix_rt::test]
async fn test_initial_setup() {
let app = App::new().configure(|cfg| configure_test_app(cfg, function_name!()));
let mut service = test::init_service(app).await;
{
let req = TestRequest::get().uri("/api/initial_setup").to_request();
let resp = service.call(req).await.unwrap();
assert!(resp.status().is_success());
let body = resp.response().body().as_u8();
let response_json: dto::InitialSetup = serde_json::from_slice(body).unwrap();
assert_eq!(
response_json,
dto::InitialSetup {
has_any_users: false
}
);
}
assert!(service
.call(initial_setup())
.await
.unwrap()
.status()
.is_success());
{
let req = TestRequest::get().uri("/api/initial_setup").to_request();
let resp = service.call(req).await.unwrap();
assert!(resp.status().is_success());
let body = resp.response().body().as_u8();
let response_json: dto::InitialSetup = serde_json::from_slice(body).unwrap();
assert_eq!(
response_json,
dto::InitialSetup {
has_any_users: true
}
);
}
}

View file

@ -1,34 +0,0 @@
use std::fs;
use std::path::PathBuf;
use crate::db::DB;
mod api;
mod swagger;
mod web;
fn configure_test_app(cfg: &mut actix_web::web::ServiceConfig, db_name: &str) {
let web_url = "/";
let web_dir_path = PathBuf::from("web");
let swagger_url = "swagger";
let mut swagger_dir_path = PathBuf::from("docs");
swagger_dir_path.push("swagger");
let mut db_path = PathBuf::new();
db_path.push("test");
db_path.push(format!("{}.sqlite", db_name));
if db_path.exists() {
fs::remove_file(&db_path).unwrap();
}
let db = DB::new(&db_path).unwrap();
super::configure_app(
cfg,
web_url,
web_dir_path.as_path(),
swagger_url,
swagger_dir_path.as_path(),
&db,
);
}

View file

@ -1,26 +0,0 @@
use actix_web::dev::Service;
use actix_web::test::TestRequest;
use actix_web::{test, App};
use function_name::named;
use super::configure_test_app;
#[named]
#[actix_rt::test]
async fn test_swagger_index() {
let app = App::new().configure(|cfg| configure_test_app(cfg, function_name!()));
let mut service = test::init_service(app).await;
let req = TestRequest::get().uri("/swagger").to_request();
let resp = service.call(req).await.unwrap();
assert!(resp.status().is_success());
}
#[named]
#[actix_rt::test]
async fn test_swagger_index_with_trailing_slash() {
let app = App::new().configure(|cfg| configure_test_app(cfg, function_name!()));
let mut service = test::init_service(app).await;
let req = TestRequest::get().uri("/swagger/").to_request();
let resp = service.call(req).await.unwrap();
assert!(resp.status().is_success());
}

View file

@ -1,16 +0,0 @@
use actix_web::dev::Service;
use actix_web::test::TestRequest;
use actix_web::{test, App};
use function_name::named;
use super::configure_test_app;
#[named]
#[actix_rt::test]
async fn test_index() {
let app = App::new().configure(|cfg| configure_test_app(cfg, function_name!()));
let mut service = test::init_service(app).await;
let req = TestRequest::get().uri("/").to_request();
let resp = service.call(req).await.unwrap();
assert!(resp.status().is_success());
}

View file

@ -2,6 +2,9 @@ mod constants;
mod dto;
mod error;
#[cfg(test)]
mod tests;
#[cfg(feature = "service-actix")]
mod actix;
#[cfg(feature = "service-actix")]

View file

@ -8,7 +8,6 @@ use super::api;
use crate::config;
use crate::ddns;
use crate::index;
use crate::service::dto;
use crate::vfs;
use super::test::get_test_environment;
@ -49,53 +48,6 @@ fn do_auth(client: &Client) {
assert_eq!(response.status(), Status::Ok);
}
#[test]
fn version() {
let env = get_test_environment("api_version.sqlite");
let client = &env.client;
let mut response = client.get("/api/version").dispatch();
assert_eq!(response.status(), Status::Ok);
let response_body = response.body_string().unwrap();
let response_json: dto::Version = serde_json::from_str(&response_body).unwrap();
assert_eq!(response_json, dto::Version { major: 4, minor: 0 });
}
#[test]
fn initial_setup() {
let env = get_test_environment("api_initial_setup.sqlite");
let client = &env.client;
{
let mut response = client.get("/api/initial_setup").dispatch();
assert_eq!(response.status(), Status::Ok);
let response_body = response.body_string().unwrap();
let response_json: dto::InitialSetup = serde_json::from_str(&response_body).unwrap();
assert_eq!(
response_json,
dto::InitialSetup {
has_any_users: false
}
);
}
complete_initial_setup(client);
{
let mut response = client.get("/api/initial_setup").dispatch();
assert_eq!(response.status(), Status::Ok);
let response_body = response.body_string().unwrap();
let response_json: dto::InitialSetup = serde_json::from_str(&response_body).unwrap();
assert_eq!(
response_json,
dto::InitialSetup {
has_any_users: true
}
);
}
}
#[test]
fn settings() {
let env = get_test_environment("api_settings.sqlite");

View file

@ -6,8 +6,4 @@ pub mod server;
#[cfg(test)]
mod api_tests;
#[cfg(test)]
mod swagger;
#[cfg(test)]
mod test;
#[cfg(test)]
mod web;
pub mod test;

View file

@ -1,19 +0,0 @@
use super::test::get_test_environment;
#[test]
fn test_index() {
use rocket::http::Status;
let env = get_test_environment("swagger_index.sqlite");
let client = &env.client;
let response = client.get("/swagger").dispatch();
assert_eq!(response.status(), Status::Ok);
}
#[test]
fn test_index_with_trailing_slash() {
use rocket::http::Status;
let env = get_test_environment("swagger_index_with_trailing_slash.sqlite");
let client = &env.client;
let response = client.get("/swagger/").dispatch();
assert_eq!(response.status(), Status::Ok);
}

View file

@ -1,5 +1,8 @@
use rocket;
use rocket::http::Status;
use rocket::local::Client;
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::fs;
use std::ops::Deref;
use std::path::PathBuf;
@ -60,3 +63,30 @@ pub fn get_test_environment(db_name: &str) -> TestEnvironment {
db,
}
}
pub type ServiceType = TestEnvironment;
pub async fn make_service(test_name: &str) -> TestEnvironment {
get_test_environment(&format!("{}.sqlite", test_name))
}
pub async fn get(service: &mut TestEnvironment, url: &str) {
let client = &service.client;
let response = client.get(url).dispatch();
assert_eq!(response.status(), Status::Ok);
}
pub async fn get_json<T: DeserializeOwned>(service: &mut TestEnvironment, url: &str) -> T {
let client = &service.client;
let mut response = client.get(url).dispatch();
assert_eq!(response.status(), Status::Ok);
let response_body = response.body_string().unwrap();
serde_json::from_str(&response_body).unwrap()
}
pub async fn put_json<T: Serialize>(service: &mut TestEnvironment, url: &str, payload: &T) {
let client = &service.client;
let body = serde_json::to_string(payload).unwrap();
let response = client.put(url).body(&body).dispatch();
assert_eq!(response.status(), Status::Ok);
}

View file

@ -1,10 +0,0 @@
use super::test::get_test_environment;
use rocket::http::Status;
#[test]
fn test_index() {
let env = get_test_environment("web_index.sqlite");
let client = &env.client;
let response = client.get("/").dispatch();
assert_eq!(response.status(), Status::Ok);
}

95
src/service/tests.rs Normal file
View file

@ -0,0 +1,95 @@
use function_name::named;
mod api;
mod static_files;
use crate::config;
use crate::service::dto;
use crate::vfs;
#[cfg(feature = "service-actix")]
pub use crate::service::actix::test::*;
#[cfg(feature = "service-rocket")]
pub use crate::service::rocket::test::*;
const TEST_USERNAME: &str = "test_user";
const TEST_PASSWORD: &str = "test_password";
const TEST_MOUNT_NAME: &str = "collection";
const TEST_MOUNT_SOURCE: &str = "test/collection";
#[named]
#[actix_rt::test]
async fn test_index() {
let mut service = make_service(function_name!()).await;
get(&mut service, "/").await;
}
#[named]
#[actix_rt::test]
async fn test_swagger_index() {
let mut service = make_service(function_name!()).await;
get(&mut service, "/swagger").await;
}
#[named]
#[actix_rt::test]
async fn test_swagger_index_with_trailing_slash() {
let mut service = make_service(function_name!()).await;
get(&mut service, "/swagger/").await;
}
async fn complete_initial_setup(service: &mut ServiceType) {
let configuration = config::Config {
album_art_pattern: None,
prefix_url: None,
reindex_every_n_seconds: None,
ydns: None,
users: Some(vec![config::ConfigUser {
name: TEST_USERNAME.into(),
password: TEST_PASSWORD.into(),
admin: true,
}]),
mount_dirs: Some(vec![vfs::MountPoint {
name: TEST_MOUNT_NAME.into(),
source: TEST_MOUNT_SOURCE.into(),
}]),
};
put_json(service, "/api/settings", &configuration).await;
}
#[named]
#[actix_rt::test]
async fn test_version() {
let mut service = make_service(function_name!()).await;
let version: dto::Version = get_json(&mut service, "/api/version").await;
assert_eq!(version, dto::Version { major: 4, minor: 0 });
}
#[named]
#[actix_rt::test]
async fn test_initial_setup() {
let mut service = make_service(function_name!()).await;
{
let initial_setup: dto::InitialSetup = get_json(&mut service, "/api/initial_setup").await;
assert_eq!(
initial_setup,
dto::InitialSetup {
has_any_users: false
}
);
}
complete_initial_setup(&mut service).await;
{
let initial_setup: dto::InitialSetup = get_json(&mut service, "/api/initial_setup").await;
assert_eq!(
initial_setup,
dto::InitialSetup {
has_any_users: true
}
);
}
}