~vpzom/lotide

fa2c17476180f3d9451f0e5916edca5eea55fc6e — Colin Reeder 4 days ago 8ea3fba + 170b686
Merge branch 'tests'
4 files changed, 471 insertions(+), 0 deletions(-)

A .build.yml
M Cargo.lock
M Cargo.toml
A tests/apub.rs
A .build.yml => .build.yml +29 -0
@@ 0,0 1,29 @@
image: alpine/3.12
packages:
  - cargo
  - openssl-dev
  - postgresql
sources: 
  - https://git.sr.ht/~vpzom/lotide
tasks:
  - install-migrant: |
      wget https://github.com/jaemk/migrant/releases/download/v0.13.0/migrant-v0.13.0-x86_64-unknown-linux-musl.tar.gz
      cd /usr/bin
      sudo tar xf ~/migrant-v0.13.0-x86_64-unknown-linux-musl.tar.gz
  - setup-db: |
      sudo -u postgres initdb -D /var/lib/postgresql/data
      sudo rc-service postgresql start
      sudo -u postgres createuser lotidetests
      sudo -u postgres createdb lotidetests1
      sudo -u postgres createdb lotidetests2

      cd ~/lotide
      env PGUSER=lotidetests PGDATABASE=lotidetests1 migrant setup
      env PGUSER=lotidetests PGDATABASE=lotidetests1 migrant apply -a
      env PGUSER=lotidetests PGDATABASE=lotidetests2 migrant setup
      env PGUSER=lotidetests PGDATABASE=lotidetests2 migrant apply -a
  - test: |
      cd lotide
      DATABASE_URL_1=postgres://lotidetests@localhost/lotidetests1 \
        DATABASE_URL_2=postgres://lotidetests@localhost/lotidetests2 \
        cargo test

M Cargo.lock => Cargo.lock +167 -0
@@ 123,6 123,12 @@ dependencies = [
]

[[package]]
name = "bumpalo"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820"

[[package]]
name = "byte-tools"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 301,6 307,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"

[[package]]
name = "encoding_rs"
version = "0.8.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8ac63f94732332f44fe654443c46f6375d1939684c17b0afb6cb56b0456e171"
dependencies = [
 "cfg-if",
]

[[package]]
name = "fake-simd"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 739,12 754,27 @@ dependencies = [
]

[[package]]
name = "ipnet"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135"

[[package]]
name = "itoa"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"

[[package]]
name = "js-sys"
version = "0.3.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85a7e2c92a4804dd459b86c339278d0fe87cf93757fae222c3fa3ae75458bc73"
dependencies = [
 "wasm-bindgen",
]

[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 831,8 861,11 @@ dependencies = [
 "lazy_static",
 "mime",
 "openssl",
 "percent-encoding",
 "postgres-types",
 "pulldown-cmark",
 "rand",
 "reqwest",
 "serde",
 "serde_derive",
 "serde_json",


@@ 878,6 911,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"

[[package]]
name = "mime_guess"
version = "2.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212"
dependencies = [
 "mime",
 "unicase",
]

[[package]]
name = "mio"
version = "0.6.22"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 1286,6 1329,42 @@ dependencies = [
]

[[package]]
name = "reqwest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12427a5577082c24419c9c417db35cfeb65962efc7675bb6b0d5f1f9d315bfe6"
dependencies = [
 "base64",
 "bytes",
 "encoding_rs",
 "futures-core",
 "futures-util",
 "http",
 "http-body",
 "hyper",
 "hyper-tls",
 "ipnet",
 "js-sys",
 "lazy_static",
 "log",
 "mime",
 "mime_guess",
 "native-tls",
 "percent-encoding",
 "pin-project-lite",
 "serde",
 "serde_json",
 "serde_urlencoded",
 "tokio",
 "tokio-tls",
 "url",
 "wasm-bindgen",
 "wasm-bindgen-futures",
 "web-sys",
 "winreg",
]

[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 1568,6 1647,7 @@ dependencies = [
 "memchr",
 "mio",
 "mio-uds",
 "num_cpus",
 "pin-project-lite",
 "slab",
 "tokio-macros",


@@ 1805,6 1885,84 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"

[[package]]
name = "wasm-bindgen"
version = "0.2.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0563a9a4b071746dd5aedbc3a28c6fe9be4586fb3fbadb67c400d4f53c6b16c"
dependencies = [
 "cfg-if",
 "serde",
 "serde_json",
 "wasm-bindgen-macro",
]

[[package]]
name = "wasm-bindgen-backend"
version = "0.2.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc71e4c5efa60fb9e74160e89b93353bc24059999c0ae0fb03affc39770310b0"
dependencies = [
 "bumpalo",
 "lazy_static",
 "log",
 "proc-macro2",
 "quote",
 "syn",
 "wasm-bindgen-shared",
]

[[package]]
name = "wasm-bindgen-futures"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95f8d235a77f880bcef268d379810ea6c0af2eacfa90b1ad5af731776e0c4699"
dependencies = [
 "cfg-if",
 "js-sys",
 "wasm-bindgen",
 "web-sys",
]

[[package]]
name = "wasm-bindgen-macro"
version = "0.2.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97c57cefa5fa80e2ba15641578b44d36e7a64279bc5ed43c6dbaf329457a2ed2"
dependencies = [
 "quote",
 "wasm-bindgen-macro-support",
]

[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841a6d1c35c6f596ccea1f82504a192a60378f64b3bb0261904ad8f2f5657556"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
 "wasm-bindgen-backend",
 "wasm-bindgen-shared",
]

[[package]]
name = "wasm-bindgen-shared"
version = "0.2.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93b162580e34310e5931c4b792560108b10fd14d64915d7fff8ff00180e70092"

[[package]]
name = "web-sys"
version = "0.3.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dda38f4e5ca63eda02c059d243aa25b5f35ab98451e518c51612cd0f1bd19a47"
dependencies = [
 "js-sys",
 "wasm-bindgen",
]

[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 1839,6 1997,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

[[package]]
name = "winreg"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69"
dependencies = [
 "winapi 0.3.9",
]

[[package]]
name = "ws2_32-sys"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"

M Cargo.toml => Cargo.toml +5 -0
@@ 41,3 41,8 @@ fluent-langneg = "0.13.0"
unic-langid = { version = "0.9.0", features = ["macros"] }
activitystreams = "0.7.0-alpha.3"
activitystreams-ext = "0.1.0-alpha.2"

[dev-dependencies]
rand = "0.7.3"
reqwest = { version = "0.10.7", features = ["blocking", "json"] }
percent-encoding = "2.1.0"

A tests/apub.rs => tests/apub.rs +270 -0
@@ 0,0 1,270 @@
use serde_derive::Deserialize;
use std::ops::Deref;

struct TestServer {
    host_url: String,
    process: std::process::Child,
}

impl TestServer {
    pub fn start(idx: u16) -> Self {
        let db_url =
            std::env::var(format!("DATABASE_URL_{}", idx)).expect("Missing DATABASE_URL_#");
        let port = 8330 + idx;
        let host_url = format!("http://localhost:{}", port);

        let child = std::process::Command::new(env!("CARGO_BIN_EXE_lotide"))
            .env("DATABASE_URL", db_url)
            .env("PORT", port.to_string())
            .env("HOST_URL_ACTIVITYPUB", format!("{}/apub", host_url))
            .env("HOST_URL_API", format!("{}/api", host_url))
            .spawn()
            .unwrap();

        let res = Self {
            host_url,
            process: child,
        };

        std::thread::sleep(std::time::Duration::from_secs(1));

        res
    }
}

impl std::ops::Drop for TestServer {
    fn drop(&mut self) {
        self.process.kill().unwrap();
    }
}

fn random_string() -> String {
    use rand::distributions::Distribution;

    rand::distributions::Alphanumeric
        .sample_iter(rand::thread_rng())
        .take(16)
        .collect()
}

fn create_account(client: &reqwest::blocking::Client, server: &TestServer) -> String {
    let resp = client
        .post(format!("{}/api/unstable/users", server.host_url).deref())
        .json(&serde_json::json!({
            "username": random_string(),
            "password": random_string(),
            "login": true
        }))
        .send()
        .unwrap()
        .error_for_status()
        .unwrap();

    #[derive(Deserialize)]
    struct JustToken {
        token: String,
    }

    let resp: JustToken = resp.json().unwrap();

    resp.token
}

struct CommunityInfo {
    id: i64,
    name: String,
}

fn create_community(
    client: &reqwest::blocking::Client,
    server: &TestServer,
    token: &str,
) -> CommunityInfo {
    let community_name = random_string();

    let resp = client
        .post(format!("{}/api/unstable/communities", server.host_url).deref())
        .bearer_auth(token)
        .json(&serde_json::json!({ "name": community_name }))
        .send()
        .unwrap()
        .error_for_status()
        .unwrap();

    let resp: serde_json::Value = resp.json().unwrap();

    CommunityInfo {
        id: resp["community"]["id"].as_i64().unwrap(),
        name: community_name,
    }
}

fn lookup_community(client: &reqwest::blocking::Client, server: &TestServer, ap_id: &str) -> i64 {
    let resp = client
        .get(
            format!(
                "{}/api/unstable/actors:lookup/{}",
                server.host_url,
                percent_encoding::utf8_percent_encode(&ap_id, percent_encoding::NON_ALPHANUMERIC)
            )
            .deref(),
        )
        .send()
        .unwrap()
        .error_for_status()
        .unwrap();

    let resp: (serde_json::Value,) = resp.json().unwrap();
    let (resp,) = resp;
    resp["id"].as_i64().unwrap()
}

lazy_static::lazy_static! {
    static ref SERVER1: TestServer = TestServer::start(1);
    static ref SERVER2: TestServer = TestServer::start(2);
}

#[test]
fn community_fetch() {
    let client = reqwest::blocking::Client::builder().build().unwrap();

    let token = create_account(&client, &SERVER1);

    let community = create_community(&client, &SERVER1, &token);

    let community_remote_id = lookup_community(
        &client,
        &SERVER2,
        &format!("{}/apub/communities/{}", SERVER1.host_url, community.id),
    );

    let resp = client
        .get(
            format!(
                "{}/api/unstable/communities/{}",
                SERVER2.host_url, community_remote_id
            )
            .deref(),
        )
        .send()
        .unwrap()
        .error_for_status()
        .unwrap();
    let resp: serde_json::Value = resp.json().unwrap();

    assert_eq!(resp["name"].as_str(), Some(community.name.as_ref()));
    assert_eq!(resp["local"].as_bool(), Some(false));
}

#[test]
fn community_follow() {
    let client = reqwest::blocking::Client::builder().build().unwrap();

    let token1 = create_account(&client, &SERVER1);

    let community = create_community(&client, &SERVER1, &token1);

    let community_remote_id = lookup_community(
        &client,
        &SERVER2,
        &format!("{}/apub/communities/{}", SERVER1.host_url, community.id),
    );

    let token2 = create_account(&client, &SERVER2);

    let resp = client
        .post(
            format!(
                "{}/api/unstable/communities/{}/follow",
                SERVER2.host_url, community_remote_id,
            )
            .deref(),
        )
        .json(&serde_json::json!({
            "try_wait_for_accept": true
        }))
        .bearer_auth(token2)
        .send()
        .unwrap()
        .error_for_status()
        .unwrap();

    let resp: serde_json::Value = resp.json().unwrap();
    assert!(resp["accepted"].as_bool().unwrap());
}

#[test]
fn community_description_update() {
    let client = reqwest::blocking::Client::builder().build().unwrap();

    let token1 = create_account(&client, &SERVER1);

    let community = create_community(&client, &SERVER1, &token1);

    let community_remote_id = lookup_community(
        &client,
        &SERVER2,
        &format!("{}/apub/communities/{}", SERVER1.host_url, community.id),
    );

    let token2 = create_account(&client, &SERVER2);

    {
        let resp = client
            .post(
                format!(
                    "{}/api/unstable/communities/{}/follow",
                    SERVER2.host_url, community_remote_id,
                )
                .deref(),
            )
            .json(&serde_json::json!({
                "try_wait_for_accept": true
            }))
            .bearer_auth(token2)
            .send()
            .unwrap()
            .error_for_status()
            .unwrap();

        let resp: serde_json::Value = resp.json().unwrap();
        assert!(resp["accepted"].as_bool().unwrap());
    }

    let new_description = random_string();

    client
        .patch(
            format!(
                "{}/api/unstable/communities/{}",
                SERVER1.host_url, community.id
            )
            .deref(),
        )
        .json(&serde_json::json!({ "description": new_description }))
        .bearer_auth(token1)
        .send()
        .unwrap()
        .error_for_status()
        .unwrap();

    std::thread::sleep(std::time::Duration::from_secs(1));

    {
        let resp = client
            .get(
                format!(
                    "{}/api/unstable/communities/{}",
                    SERVER2.host_url, community_remote_id,
                )
                .deref(),
            )
            .send()
            .unwrap()
            .error_for_status()
            .unwrap();

        let resp: serde_json::Value = resp.json().unwrap();
        assert_eq!(resp["description"].as_str(), Some(new_description.as_ref()));
    }
}