Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
DATABASE_URL=postgresql://user:[email protected]:8001/godsaeng
REDIS_URL=redis://127.0.0.1:6379
SECRET_KEY=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
23 changes: 23 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,36 @@ jobs:
test:
name: ✅ Test Suite
runs-on: ubuntu-latest
services:
postgres:
image: postgres:14
ports:
- 8001:5432
redis:
image: redis:7
ports:
- 6379:6379
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
- uses: Swatinem/rust-cache@v1
with:
key: sqlx-0.6.2
- name: Install sqlx-cli
run:
cargo install sqlx-cli
--version=0.6.2
--features rustls,postgres
--no-default-features
--locked
- name: Migrate database
run: |
sudo apt-get install libpq-dev -y
sqlx database create
sqlx migrate run
- name: Install cargo-nextest
uses: baptiste0928/cargo-install@v1
with:
Expand Down
5 changes: 1 addition & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,4 @@
Cargo.lock

# These are backup files generated by rustfmt
**/*.rs.bk

## env file
.env
**/*.rs.bk
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,8 @@ dotenv = "0.15.0"
serde = { version = "1.0.152", features = ["derive"] }
sqlx = { version = "0.6.2", features = ["runtime-async-std-native-tls", "postgres", "chrono"] }
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }

[dev-dependencies]
reqwest = { version = "0.11.14", features = ["json", "cookies"] }
serde_json = "1.0.91"
uuid = { version = "1", features = ["v4"] }
166 changes: 166 additions & 0 deletions tests/event_check.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
use reqwest::{Client, Response};
use serde::Deserialize;
use serde_json::{Map, Number, Value};

mod helper;
use helper::{login, test_run, TestApp};

#[derive(Deserialize)]
struct IdRow {
pub id: i32,
}

async fn create_event(
app: &TestApp,
client: &Client,
note: &str,
event_date: &str,
) -> Result<Response, std::io::Error> {
let mut map = Map::new();
map.insert("note".to_string(), Value::String(note.to_string()));
map.insert(
"event_date".to_string(),
Value::String(event_date.to_string()),
);
let response = client
.post(&format!("{}/event", &app.address))
.json(&map)
.send()
.await
.expect("Failed to execute request.");
Ok(response)
}

async fn patch_event(
app: &TestApp,
client: &Client,
id: i32,
new_note: &str,
new_event_date: &str,
) -> Result<Response, std::io::Error> {
let mut map = Map::new();
map.insert("id".to_string(), Value::Number(Number::from(id)));
map.insert("new_note".to_string(), Value::String(new_note.to_string()));
map.insert(
"new_event_date".to_string(),
Value::String(new_event_date.to_string()),
);
let response = client
.patch(&format!("{}/event", &app.address))
.json(&map)
.send()
.await
.expect("Failed to execute request.");
Ok(response)
}

async fn delete_event(app: &TestApp, client: &Client, id: i32) -> Result<Response, std::io::Error> {
let mut map = Map::new();
map.insert("id".to_string(), Value::Number(Number::from(id)));
let response = client
.delete(&format!("{}/event", &app.address))
.json(&map)
.send()
.await
.expect("Failed to execute request.");
Ok(response)
}

#[tokio::test]
async fn create_event_works() {
let app = test_run().await;
let client = reqwest::Client::builder()
.cookie_store(true)
.build()
.expect("Failed to create Client");

login(&app, &client, "admin", "secret")
.await
.expect("Failed to login as admin");

let response: Response = create_event(&app, &client, "test_note", "2022-01-01")
.await
.expect("Failed to create event.");
assert!(response.status().is_success());

let ret: IdRow = response.json().await.expect("Failed to get the json");
assert_eq!(ret.id, 1)
}

#[tokio::test]
async fn patch_event_works() {
let app = test_run().await;
let client = reqwest::Client::builder()
.cookie_store(true)
.build()
.expect("Failed to create Client");

login(&app, &client, "admin", "secret")
.await
.expect("Failed to login as admin");

let event_id = {
let response: Response = create_event(&app, &client, "test_note", "2022-01-01")
.await
.expect("Failed to create event.");
let ret: IdRow = response.json().await.expect("Failed to get the json");
ret.id
};
assert_eq!(event_id, 1);

let response: Response = patch_event(&app, &client, event_id, "new_note", "2023-01-01")
.await
.expect("Failed to patch event");
assert!(response.status().is_success());
}

#[tokio::test]
async fn patch_event_returns_a_400_for_invalid_id() {
let app = test_run().await;
let client = reqwest::Client::builder()
.cookie_store(true)
.build()
.expect("Failed to create Client");

login(&app, &client, "admin", "secret")
.await
.expect("Failed to login as admin");
let event_id = {
let response: Response = create_event(&app, &client, "test_note", "2022-01-01")
.await
.expect("Failed to create event.");
let ret: IdRow = response.json().await.expect("Failed to get the json");
ret.id
};
assert_eq!(event_id, 1);

let response: Response = patch_event(&app, &client, event_id + 1, "new_note", "2023-01-01")
.await
.expect("Failed to patch event");
assert_eq!(400, response.status().as_u16());
}

#[tokio::test]
async fn delete_event_works() {
let app = test_run().await;
let client = reqwest::Client::builder()
.cookie_store(true)
.build()
.expect("Failed to create Client");

login(&app, &client, "admin", "secret")
.await
.expect("Failed to login as admin");
let event_id = {
let response: Response = create_event(&app, &client, "test_note", "2022-01-01")
.await
.expect("Failed to create event.");
let ret: IdRow = response.json().await.expect("Failed to get the json");
ret.id
};
let response: Response = delete_event(&app, &client, event_id)
.await
.expect("Failed to patch event");

assert!(response.status().is_success());
}
77 changes: 77 additions & 0 deletions tests/helper.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use actix_session::storage::RedisSessionStore;
use dotenv::dotenv;
use godsaeng_backend::runner::run;
use reqwest::{Client, Response};
use serde_json::{Map, Value};
use sqlx::{Connection, Executor, PgConnection, PgPool};
use std::net::TcpListener;

use uuid::Uuid;

pub struct TestApp {
pub address: String,
pub db_pool: PgPool,
}

pub async fn test_run() -> TestApp {
let listener = TcpListener::bind("127.0.0.1:0").expect("Failed to bind random port");
let port = listener.local_addr().unwrap().port();
let address = format!("http://127.0.0.1:{port}");

dotenv().ok();
let db_url = std::env::var("TEST_DATABASE_URL").expect("DATABASE_URL must be set");
let test_database = format!("{db_url}/postgres");

let mut connection = PgConnection::connect(&test_database)
.await
.expect("Failed to connect to Postgres");

let database_name = Uuid::new_v4().to_string();

connection
.execute(&*format!(r#"CREATE DATABASE "{database_name}";"#))
.await
.expect("Failed to create database.");

let test_database_url = format!("{db_url}/{database_name}");
println!("{test_database_url}");
let connection_pool = PgPool::connect(&test_database_url)
.await
.expect("Failed to connect to Postgres");

sqlx::migrate!("./migrations")
.run(&connection_pool)
.await
.expect("Failed to migrate the database");
let store = {
let redis_url = std::env::var("REDIS_URL").expect("REDI must be set");
RedisSessionStore::new(redis_url).await.unwrap()
};
let secret_key = std::env::var("SECRET_KEY").expect("SECRET_KEY must be set");
let server =
run(listener, connection_pool.clone(), store, secret_key).expect("Failed to bind address");
tokio::spawn(server);

TestApp {
address,
db_pool: connection_pool,
}
}

pub async fn login(
app: &TestApp,
client: &Client,
name: &str,
password: &str,
) -> Result<Response, std::io::Error> {
let mut map = Map::new();
map.insert("name".to_string(), Value::String(name.to_string()));
map.insert("password".to_string(), Value::String(password.to_string()));
let response = client
.post(&format!("{}/login", &app.address))
.json(&map)
.send()
.await
.expect("Failed to execute request.");
Ok(response)
}
Loading