Skip to content

Commit

Permalink
Merge pull request #51 from traP-jp/feat#46-users-integration-test
Browse files Browse the repository at this point in the history
Feat#46 users integration test
  • Loading branch information
kenken714 authored Dec 13, 2024
2 parents 6c585ae + cb91316 commit 90ac17a
Show file tree
Hide file tree
Showing 14 changed files with 431 additions and 5 deletions.
Binary file removed .DS_Store
Binary file not shown.
1 change: 1 addition & 0 deletions .env.dev
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export DB_HOSTNAME="localhost"
export DB_PORT="3306"
export DB_DATABASE="world"
export RUST_LOG="debug"
export DATABASE_URL="mariadb://$DB_USERNAME:$DB_PASSWORD@$DB_HOSTNAME:$DB_PORT/$DB_DATABASE"

export MAIL_ADDRESS="[email protected]"
export MAIL_PASSWORD="**** **** **** ****"
Expand Down
12 changes: 11 additions & 1 deletion .github/workflows/develop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ env:
jobs:
test:
runs-on: ubuntu-latest
services:
mariadb:
image: mariadb:latest
env:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: world
TZ: Asia/Tokyo
ports:
- 3306:3306
options: --health-cmd="mariadb-admin ping" --health-interval=10s --health-timeout=5s --health-retries=3
steps:
- name: Checkout
uses: actions/checkout@v4
Expand All @@ -32,7 +42,7 @@ jobs:
- name: Build test
run: cargo build --release --verbose
- name: Run test
run: cargo test --verbose
run: source .env.dev && cargo test --verbose
- name: Lint with clippy
run: cargo clippy --all-targets --all-features
- name: Check formatting
Expand Down
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ edition = "2021"

[dependencies]
axum = "0.7"
http-body-util = "0.1.0"
axum-extra = { version = "0.9", features = [ "typed-header" ] }
tokio = { version = "1.0", features = ["full"] }
serde = { version = "1.0", features = [ "derive" ] }
serde_json = "1.0"
tower = "0.4"
tower = { version = "0.4", features = ["full"] }
tower-http = { version = "0.5.0", features = ["add-extension", "trace", "fs"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
Expand Down
10 changes: 7 additions & 3 deletions src/handler/users.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,14 +178,18 @@ pub async fn put_me(
self_introduction: body.self_introduction.unwrap_or(user.self_introduction),
};

let icon_url = new_body.icon_url.clone();

state
.update_user(user.display_id, new_body)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;

Ok(Json(serde_json::json!({"iconUrl": icon_url})))
let new_user = state
.get_user_by_display_id(display_id)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
.ok_or(StatusCode::INTERNAL_SERVER_ERROR)?;

Ok(Json(new_user))
}

pub async fn get_user(
Expand Down
12 changes: 12 additions & 0 deletions src/repository.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,18 @@ impl Repository {

Ok(())
}

pub async fn create_by_pool(pool: sqlx::MySqlPool) -> anyhow::Result<Self> {
let session_store =
MySqlSessionStore::from_client(pool.clone()).with_table_name("user_sessions");
session_store.migrate().await?;

Ok(Self {
pool,
session_store,
bcrypt_cost: bcrypt::DEFAULT_COST,
})
}
}

fn get_option_from_env() -> anyhow::Result<MySqlConnectOptions> {
Expand Down
2 changes: 2 additions & 0 deletions src/repository/users.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ use super::Repository;
#[derive(Debug, Clone, PartialEq, sqlx::Type, Serialize)]
#[repr(i32)]
pub enum UserRole {
#[serde(rename = "commonUser")]
common_user = 0,
#[serde(rename = "traPUser")]
traP_user = 1,
admin = 2,
}
Expand Down
1 change: 1 addition & 0 deletions tests/test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mod users;
50 changes: 50 additions & 0 deletions tests/users/common/check.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use super::remove_field;
use serde_json::{json, Value};

pub fn users_check_by_id(id: i64, resp_json: &mut Value) -> anyhow::Result<()> {
let users_json = match id {
1 => json!({
"id": "11111111-1111-1111-1111-111111111111",
"displayId": 1,
"name": "test_user_1",
"traqId": null,
"githubId": "test_github_id_1",
"iconUrl": null,
"githubLink": "https://github.com/test_user_1",
"xLink": null,
"selfIntroduction": "test_self_introduction_1",
"role": "commonUser",
}),
2 => json!({
"id": "22222222-2222-2222-2222-222222222222",
"displayId": 2,
"name": "test_user_2",
"traqId": "test_traq_id_2",
"githubId": null,
"iconUrl": null,
"githubLink": null,
"xLink": "https://x.com/test_user_2",
"selfIntroduction": "",
"role": "traPUser",
}),
3 => json!({
"id": "33333333-3333-3333-3333-333333333333",
"displayId": 3,
"name": "test_user_3",
"traqId": null,
"githubId": null,
"iconUrl": "https://icon.com/test_user_3",
"githubLink": null,
"xLink": null,
"selfIntroduction": "",
"role": "admin",
}),
_ => return Err(anyhow::anyhow!("Invalid id")),
};
let ignore_field = vec!["createdAt", "updatedAt"];

remove_field(resp_json, &ignore_field);
assert_eq!(resp_json, &users_json);

Ok(())
}
31 changes: 31 additions & 0 deletions tests/users/common/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use axum::{
body::Body,
http::{request, Request},
};

pub mod check;

pub trait RequestBuilderExt {
fn json(self, json: serde_json::Value) -> Request<Body>;
}

impl RequestBuilderExt for request::Builder {
fn json(self, json: serde_json::Value) -> Request<Body> {
self.header("Content-Type", "application/json")
.body(Body::from(json.to_string()))
.unwrap()
}
}

pub fn remove_field(json: &mut serde_json::Value, ignore_fields: &Vec<&str>) {
match json {
serde_json::Value::Object(obj) => {
for field in ignore_fields {
obj.remove(*field);
}
}
_ => {
panic!("Invalid JSON format");
}
}
}
41 changes: 41 additions & 0 deletions tests/users/fixtures/common.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
INSERT INTO users
(id, name, role, github_id, github_link, self_introduction, email)
VALUES
(
UNHEX(REPLACE('11111111-1111-1111-1111-111111111111','-','')),
'test_user_1',
0,
"test_github_id_1",
"https://github.com/test_user_1",
"test_self_introduction_1",
"[email protected]"
);

INSERT INTO users_passwords
(user_id, password)
VALUES
(
UNHEX(REPLACE('11111111-1111-1111-1111-111111111111','-','')),
"test_password_1"
);

INSERT INTO
users (id, name, role, traq_id, x_link)
VALUES
(
UNHEX(REPLACE('22222222-2222-2222-2222-222222222222','-','')),
'test_user_2',
1,
"test_traq_id_2",
"https://x.com/test_user_2"
);

INSERT INTO users
(id, name, role, icon_url)
VALUES
(
UNHEX(REPLACE('33333333-3333-3333-3333-333333333333','-','')),
'test_user_3',
2,
"https://icon.com/test_user_3"
);
116 changes: 116 additions & 0 deletions tests/users/get_users.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
use std::borrow::BorrowMut;

use super::common::check::users_check_by_id;
use axum::{
body::Body,
http::{self, Request},
};
use http_body_util::BodyExt;
use serde_json::Value;
use tower::ServiceExt;
use trao_judge_backend::{make_router, Repository};

#[sqlx::test(fixtures("common"))]
async fn get_user_by_id(pool: sqlx::MySqlPool) -> anyhow::Result<()> {
let state = Repository::create_by_pool(pool).await?;
let mut app = make_router(state);

let tests = vec![1, 2, 3];

for id in tests {
let response = app
.borrow_mut()
.oneshot(
Request::builder()
.method(http::Method::GET)
.uri(format!("/users/{}", id))
.body(Body::empty())?,
)
.await?;

assert_eq!(response.status(), 200);

let mut resp_json: Value =
serde_json::from_slice(&response.into_body().collect().await?.to_bytes())?;

users_check_by_id(id, &mut resp_json)?;
}

Ok(())
}

#[sqlx::test(fixtures("common"))]
async fn get_user_by_id_not_found(pool: sqlx::MySqlPool) -> anyhow::Result<()> {
let state = Repository::create_by_pool(pool).await?;
let mut app = make_router(state);

let not_found_case = vec![0, 4, 10, 1000000];
for id in not_found_case {
let response = app
.borrow_mut()
.oneshot(
Request::builder()
.method(http::Method::GET)
.uri(format!("/users/{}", id))
.body(Body::empty())?,
)
.await?;

assert_eq!(response.status(), 404);
}

Ok(())
}

#[sqlx::test(fixtures("common"))]
async fn get_user_me(pool: sqlx::MySqlPool) -> anyhow::Result<()> {
let state = Repository::create_by_pool(pool).await?;
let mut app = make_router(state.clone());

let tests = vec![1, 2, 3];

for id in tests {
let session_id = state
.create_session(state.get_user_by_display_id(id).await?.unwrap())
.await?;

let response = app
.borrow_mut()
.oneshot(
Request::builder()
.method(http::Method::GET)
.uri("/users/me")
.header("Cookie", format!("session_id={}", session_id))
.body(Body::empty())?,
)
.await?;
assert_eq!(response.status(), 200);

let mut resp_json: Value =
serde_json::from_slice(&response.into_body().collect().await?.to_bytes())?;

users_check_by_id(id, &mut resp_json)?;
}

Ok(())
}

#[sqlx::test(fixtures("common"))]
async fn get_user_me_unauthorized(pool: sqlx::MySqlPool) -> anyhow::Result<()> {
let state = Repository::create_by_pool(pool).await?;
let mut app = make_router(state.clone());

// Test unauthorized case
let response = app
.borrow_mut()
.oneshot(
Request::builder()
.method(http::Method::GET)
.uri("/users/me")
.body(Body::empty())?,
)
.await?;
assert_eq!(response.status(), 401);

Ok(())
}
4 changes: 4 additions & 0 deletions tests/users/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub mod common;

mod get_users;
mod put_users;
Loading

0 comments on commit 90ac17a

Please sign in to comment.