diff --git a/Cargo.lock b/Cargo.lock
index d8d3bc7a..3101ab19 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1355,9 +1355,21 @@ dependencies = [
  "pin-project-lite",
  "signal-hook-registry",
  "slab",
+ "tokio-macros",
  "winapi 0.3.8",
 ]
 
+[[package]]
+name = "tokio-macros"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389"
+dependencies = [
+ "proc-macro2 1.0.10",
+ "quote 1.0.3",
+ "syn 1.0.17",
+]
+
 [[package]]
 name = "tokio-rustls"
 version = "0.12.2"
diff --git a/Cargo.toml b/Cargo.toml
index 92a66812..7618ac0f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -26,5 +26,5 @@ serde_json = "1.0.50"
 ruma-signatures = { git = "https://github.com/ruma/ruma-signatures.git" }
 ruma-federation-api = "0.0.1"
 serde = "1.0.106"
-tokio = "0.2.16"
+tokio = { version = "0.2.16", features = ["macros"] } #rt-threaded
 rand = "0.7.3"
diff --git a/src/database.rs b/src/database.rs
index a19ec43e..f17e76fd 100644
--- a/src/database.rs
+++ b/src/database.rs
@@ -1,6 +1,7 @@
 use crate::utils;
 use directories::ProjectDirs;
 use sled::IVec;
+use std::fs::remove_dir_all;
 
 pub struct MultiValue(sled::Tree);
 
@@ -65,6 +66,16 @@ pub struct Database {
 }
 
 impl Database {
+    /// Tries to remove the old database but ignores all errors.
+    pub fn try_remove(hostname: &str) {
+        let mut path = ProjectDirs::from("xyz", "koesters", "matrixserver")
+            .unwrap()
+            .data_dir()
+            .to_path_buf();
+        path.push(hostname);
+        let _ = remove_dir_all(path);
+    }
+
     /// Load an existing database or create a new one.
     pub fn load_or_create(hostname: &str) -> Self {
         let mut path = ProjectDirs::from("xyz", "koesters", "matrixserver")
diff --git a/src/main.rs b/src/main.rs
index 4b1f72e8..e7b49c76 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -6,6 +6,9 @@ mod pdu;
 mod ruma_wrapper;
 mod utils;
 
+#[cfg(test)]
+mod test;
+
 pub use data::Data;
 pub use database::Database;
 pub use pdu::PduEvent;
@@ -31,6 +34,7 @@ use ruma_client_api::{
             get_avatar_url, get_display_name, get_profile, set_avatar_url, set_display_name,
         },
         push::get_pushrules_all,
+        read_marker::set_read_marker,
         room::create_room,
         session::{get_login_types, login},
         state::{create_state_event_for_empty_key, create_state_event_for_key},
@@ -159,9 +163,7 @@ fn login_route(data: State<Data>, body: Ruma<login::Request>) -> MatrixResult<lo
                 username = format!("@{}:{}", username, data.hostname());
             }
             if let Ok(user_id) = (*username).try_into() {
-                if !data.user_exists(&user_id) {}
-
-                // Check password
+                // Check password (this also checks if the user exists
                 if let Some(correct_password) = data.password_get(&user_id) {
                     if password == correct_password {
                         // Success!
@@ -464,6 +466,15 @@ fn upload_keys_route(
     }))
 }
 
+#[post("/_matrix/client/r0/rooms/<_room_id>/read_markers", data = "<body>")]
+fn set_read_marker_route(
+    data: State<Data>,
+    body: Ruma<set_read_marker::Request>,
+    _room_id: String,
+) -> MatrixResult<set_read_marker::Response> {
+    MatrixResult(Ok(set_read_marker::Response))
+}
+
 #[post("/_matrix/client/r0/createRoom", data = "<body>")]
 fn create_room_route(
     data: State<Data>,
@@ -765,16 +776,7 @@ fn options_route(_segments: PathBuf) -> MatrixResult<create_message_event::Respo
     }))
 }
 
-fn main() {
-    // Log info by default
-    if let Err(_) = std::env::var("RUST_LOG") {
-        std::env::set_var("RUST_LOG", "matrixserver=debug,info");
-    }
-    pretty_env_logger::init();
-
-    let data = Data::load_or_create("matrixtesting.koesters.xyz");
-    data.debug();
-
+fn setup_rocket(data: Data) -> rocket::Rocket {
     rocket::ignite()
         .mount(
             "/",
@@ -796,6 +798,7 @@ fn main() {
                 set_presence_route,
                 get_keys_route,
                 upload_keys_route,
+                set_read_marker_route,
                 create_room_route,
                 get_alias_route,
                 join_room_by_id_route,
@@ -810,6 +813,17 @@ fn main() {
             ],
         )
         .manage(data)
-        .launch()
-        .unwrap();
+}
+
+fn main() {
+    // Log info by default
+    if let Err(_) = std::env::var("RUST_LOG") {
+        std::env::set_var("RUST_LOG", "matrixserver=debug,info");
+    }
+    pretty_env_logger::init();
+
+    let data = Data::load_or_create("matrixtesting.koesters.xyz");
+    data.debug();
+
+    setup_rocket(data).launch().unwrap();
 }
diff --git a/src/test.rs b/src/test.rs
new file mode 100644
index 00000000..6131eb2d
--- /dev/null
+++ b/src/test.rs
@@ -0,0 +1,32 @@
+use super::*;
+use rocket::{local::Client, http::Status};
+
+fn setup_client() -> Client {
+    Database::try_remove("temp");
+    let data = Data::load_or_create("temp");
+
+    let rocket = setup_rocket(data);
+    Client::new(rocket).expect("valid rocket instance")
+}
+
+#[tokio::test]
+async fn register_login() {
+    let client = setup_client();
+    let mut response = client
+        .post("/_matrix/client/r0/register?kind=user")
+        .body(
+            r#"{
+    "username": "cheeky_monkey",
+    "password": "ilovebananas",
+    "device_id": "GHTYAJCE",
+    "initial_device_display_name": "Jungle Phone",
+    "inhibit_login": false
+            }"#,
+        )
+        .dispatch().await;
+    let body = serde_json::to_value(&response.body_string().await.unwrap()).unwrap();
+
+    assert_eq!(response.status().code, 401);
+    assert!(dbg!(&body["flows"]).as_array().unwrap().len() > 0);
+    assert!(body["session"].as_str().unwrap().len() > 0);
+}