Add a couple basic tests

This commit is contained in:
Raphaël Thériault 2020-10-01 15:14:13 -04:00
parent 2a0d41be07
commit 6da60a96b6
7 changed files with 157 additions and 17 deletions

View File

@ -42,7 +42,7 @@ jobs:
- uses: actions/cache@v2
with:
path: /tmp/.buildx-cache
key: buildx-${{ github.sha }}
key: buildx-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
buildx-
@ -55,7 +55,7 @@ jobs:
cache-to: type=local,dest=/tmp/.buildx-cache
labels: |
org.opencontainers.image.title=${{ github.event.repository.name }}
org.opencontainers.image.description=${{ github.event.repository.description }}
org.opencontainers.image.description='${{ github.event.repository.description }}'
org.opencontainers.image.url=${{ github.event.repository.html_url }}
org.opencontainers.image.source=${{ github.event.repository.clone_url }}
org.opencontainers.image.version=${{ steps.prep.outputs.version }}

12
Cargo.lock generated
View File

@ -1182,6 +1182,17 @@ dependencies = [
"num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pin-project-lite 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-macros 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-macros"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -1666,6 +1677,7 @@ dependencies = [
"checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
"checksum time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
"checksum tokio 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)" = "5d34ca54d84bf2b5b4d7d31e901a8464f7b60ac145a284fba25ceb801f2ddccd"
"checksum tokio-macros 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389"
"checksum tokio-rustls 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e12831b255bcfa39dc0436b01e19fea231a37db570686c06ee72c423479f889a"
"checksum tokio-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499"
"checksum toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a"

View File

@ -28,7 +28,7 @@ serde = { version = "1.0.116", features = ["derive"] }
serde_json = "1.0.57"
sled = "0.34.4"
structopt = "0.3.18"
tokio = { version = "0.2.22", features = ["blocking", "fs", "rt-threaded"] }
tokio = { version = "0.2.22", features = ["blocking", "fs", "macros", "rt-threaded"] }
tracing = "0.1.19"
tracing-futures = "0.2.4"
tracing-subscriber = "0.2.12"

View File

@ -31,7 +31,7 @@ pub fn required(
warp::header::header("Authorization").and_then(move |header| user(header, db, config))
}
#[tracing::instrument(level = "debug")]
#[tracing::instrument(level = "debug", skip(db))]
async fn user(header: String, db: &Db, config: &Config) -> Result<User, Rejection> {
if &header[..5] != "Basic" {
return Err(crate::reject::unauthorized());

View File

@ -4,6 +4,7 @@ use chrono::{DateTime, Utc};
use rand::{distributions::Alphanumeric, Rng};
use serde::{Deserialize, Serialize};
use sled::Db;
use std::fmt;
use tokio::task;
#[tracing::instrument(level = "debug")]
@ -171,13 +172,22 @@ pub fn random_id(length: usize, db: &Db) -> Result<String> {
})
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[derive(Clone, Deserialize, Serialize)]
pub struct User {
pub id: String,
pub admin: bool,
pub password_hash: String,
}
impl fmt::Debug for User {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("User")
.field("id", &self.id)
.field("admin", &self.admin)
.finish()
}
}
#[derive(Deserialize, Serialize)]
struct DbUser {
admin: bool,

View File

@ -6,7 +6,10 @@ use crate::{
use bytes::Bytes;
use sled::Db;
use warp::reply::Response;
use warp::{http::Uri, Filter, Rejection, Reply};
use warp::{
http::{StatusCode, Uri},
Filter, Rejection, Reply,
};
pub fn handler(
config: &'static Config,
@ -20,14 +23,14 @@ pub fn handler(
.and(warp::post())
.and(crate::auth::required(db, config))
.and(warp::body::bytes())
.and(warp::header("Content-Type"))
.and(warp::header::optional("Content-Type"))
.and(warp::header::optional("X-ID-Length"))
.and_then(move |user, data, mime, len| post_file(user, data, mime, len, db));
let put_file = warp::path!("f" / String)
.and(warp::put())
.and(crate::auth::required(db, config))
.and(warp::body::bytes())
.and(warp::header("Content-Type"))
.and(warp::header::optional("Content-Type"))
.and_then(move |id, user, data, mime| put_file(id, user, data, mime, db));
let post_link = warp::path!("l")
@ -88,7 +91,7 @@ async fn filite(id: String, db: &Db) -> Result<impl Reply, Rejection> {
async fn post_file(
user: User,
data: Bytes,
mime: String,
mime: Option<String>,
len: Option<usize>,
db: &Db,
) -> Result<impl Reply, Rejection> {
@ -101,13 +104,19 @@ async fn put_file(
id: String,
user: User,
data: Bytes,
mime: String,
mime: Option<String>,
db: &Db,
) -> Result<impl Reply, Rejection> {
crate::db::insert_file(&id, user.id, data.to_vec(), mime, db)
.or_500()?
.or_409()?;
Ok(id)
crate::db::insert_file(
&id,
user.id,
data.to_vec(),
mime.unwrap_or_else(|| "application/octet-stream".to_owned()),
db,
)
.or_500()?
.or_409()?;
Ok(warp::reply::with_status(id, StatusCode::CREATED))
}
#[tracing::instrument(level = "debug", skip(db))]
@ -126,7 +135,7 @@ async fn put_link(id: String, user: User, location: Uri, db: &Db) -> Result<impl
crate::db::insert_link(&id, user.id, location.to_string(), db)
.or_500()?
.or_409()?;
Ok(id)
Ok(warp::reply::with_status(id, StatusCode::CREATED))
}
#[tracing::instrument(level = "debug", skip(db))]
@ -145,5 +154,5 @@ async fn put_text(id: String, user: User, data: String, db: &Db) -> Result<impl
crate::db::insert_text(&id, user.id, data, db)
.or_500()?
.or_409()?;
Ok(id)
Ok(warp::reply::with_status(id, StatusCode::CREATED))
}

View File

@ -1 +1,110 @@
// TODO
use crate::config::Config;
use sled::Db;
use tracing_subscriber::fmt::format::FmtSpan;
use warp::http::StatusCode;
fn setup() -> (&'static Config, &'static Db) {
tracing_subscriber::fmt()
.with_env_filter("debug")
.with_span_events(FmtSpan::CLOSE)
.try_init()
.ok();
let config = Box::leak(Box::new(Default::default()));
let db = Box::leak(Box::new(
sled::Config::default().temporary(true).open().unwrap(),
));
crate::db::insert_user("user", "password", true, db, config).unwrap();
(config, db)
}
const AUTH: &str = "Basic dXNlcjpwYXNzd29yZA==";
#[tokio::test(core_threads = 2)]
async fn file() {
let (config, db) = setup();
let filter = crate::routes::handler(config, db);
let value = b"file";
let reply = warp::test::request()
.path("/f")
.method("POST")
.body(value)
.header("Authorization", AUTH)
.reply(&filter)
.await;
assert_eq!(reply.status(), StatusCode::CREATED);
let id = std::str::from_utf8(reply.body()).unwrap();
let reply = warp::test::request()
.path(&format!("/{}", id))
.reply(&filter)
.await;
assert_eq!(reply.status(), StatusCode::OK);
assert_eq!(
reply
.headers()
.get("Content-Type")
.unwrap()
.to_str()
.unwrap(),
"application/octet-stream"
);
assert_eq!(reply.body().as_ref(), value);
}
#[tokio::test(core_threads = 2)]
async fn link() {
let (config, db) = setup();
let filter = crate::routes::handler(config, db);
let value = "https://google.com/";
let reply = warp::test::request()
.path("/l")
.method("POST")
.body(value)
.header("Authorization", AUTH)
.reply(&filter)
.await;
assert_eq!(reply.status(), StatusCode::CREATED);
let id = std::str::from_utf8(reply.body()).unwrap();
let reply = warp::test::request()
.path(&format!("/{}", id))
.reply(&filter)
.await;
assert_eq!(reply.status(), StatusCode::TEMPORARY_REDIRECT);
assert_eq!(
reply.headers().get("Location").unwrap().to_str().unwrap(),
value
);
}
#[tokio::test(core_threads = 2)]
async fn text() {
let (config, db) = setup();
let filter = crate::routes::handler(config, db);
let value = "text";
let reply = warp::test::request()
.path("/t")
.method("POST")
.body(value)
.header("Authorization", AUTH)
.reply(&filter)
.await;
assert_eq!(reply.status(), StatusCode::CREATED);
let id = std::str::from_utf8(reply.body()).unwrap();
let reply = warp::test::request()
.path(&format!("/{}", id))
.reply(&filter)
.await;
assert_eq!(reply.status(), StatusCode::OK);
assert_eq!(std::str::from_utf8(reply.body()).unwrap(), value);
}