Koucha/koucha/src/db/user.rs

209 lines
5 KiB
Rust
Raw Normal View History

2026-01-20 16:08:36 -08:00
use crate::{
Result,
AdapterPool,
db::{
UserId,
Feed,
feed::UnparsedFeed,
},
2026-01-20 16:08:36 -08:00
};
2026-01-21 12:47:47 -08:00
pub struct UnparsedUser {
pub id: i64,
2026-01-20 16:08:36 -08:00
pub name: String,
}
2026-01-21 12:47:47 -08:00
impl UnparsedUser {
pub fn parse(self) -> Result<User> {
Ok(User {
id: UserId(self.id),
name: self.name
})
}
}
pub struct User {
id: UserId,
name: String,
}
2026-01-20 16:08:36 -08:00
impl User {
2026-01-21 12:47:47 -08:00
pub fn id(&self) -> UserId { self.id }
pub fn name(&self) -> &str { &self.name }
pub async fn get(pool: &AdapterPool, id: UserId) -> Result<Self> {
2026-01-21 12:47:47 -08:00
let user = sqlx::query_as!(
UnparsedUser,
"SELECT id, name FROM users WHERE id = ?",
id.0
).fetch_one(&pool.0).await?.parse();
2026-01-21 12:47:47 -08:00
user
}
2026-01-26 15:00:52 -08:00
pub async fn get_all(pool: &AdapterPool) -> Result<Vec<Self>> {
2026-01-21 12:47:47 -08:00
let users: Result<Vec<Self>> = sqlx::query_as!(
UnparsedUser,
2026-01-20 16:08:36 -08:00
"SELECT id, name FROM users"
).fetch_all(&pool.0).await?.into_iter().map(UnparsedUser::parse).collect();
2026-01-20 16:08:36 -08:00
2026-01-21 12:47:47 -08:00
users
2026-01-20 16:08:36 -08:00
}
pub async fn create(pool: &AdapterPool, name: &str) -> Result<Self> {
2026-01-20 16:08:36 -08:00
let result = sqlx::query!(
"INSERT INTO users (name)
2026-01-20 16:08:36 -08:00
VALUES (?)
RETURNING id, name",
name
).fetch_one(&pool.0).await?;
2026-01-20 16:08:36 -08:00
Ok(Self {
id: UserId(result.id),
name: result.name,
})
}
pub async fn update_name(
pool: &AdapterPool, id: UserId, new_name: &str
2026-01-20 16:08:36 -08:00
) -> Result<()> {
sqlx::query!(
"UPDATE users SET name = ? WHERE id = ?",
new_name, id.0
).execute(&pool.0).await?;
2026-01-20 16:08:36 -08:00
Ok(())
}
pub async fn get_feeds(&self, pool: &AdapterPool) -> Result<Vec<Feed>> {
2026-01-21 12:47:47 -08:00
let feeds: Result<Vec<Feed>> = sqlx::query_as!(
UnparsedFeed,
"SELECT id, title FROM feeds WHERE user_id = ?",
self.id.0
).fetch_all(&pool.0).await?.into_iter()
2026-01-21 12:47:47 -08:00
.map(UnparsedFeed::parse).collect();
2026-01-20 16:08:36 -08:00
2026-01-21 12:47:47 -08:00
feeds
2026-01-20 16:08:36 -08:00
}
2026-01-21 12:47:47 -08:00
}
2026-01-20 16:08:36 -08:00
2026-01-21 12:47:47 -08:00
#[cfg(test)]
mod tests {
use super::*;
2026-01-26 15:00:52 -08:00
use crate::{
db::Feed,
test_utils::{
USERNAME, USERNAME2, FEED_TITLE, FEED_TITLE2,
setup_adapter,
},
};
#[test]
fn parse() {
const UID: i64 = 1;
let unparsed_user = UnparsedUser {
id: UID,
name: USERNAME.to_string(),
};
let user = unparsed_user.parse().unwrap();
assert_eq!(user.id.0, UID);
assert_eq!(user.name, USERNAME);
2026-01-21 12:47:47 -08:00
}
2026-01-26 15:00:52 -08:00
2026-01-21 12:47:47 -08:00
#[tokio::test]
2026-01-26 15:00:52 -08:00
async fn get() {
let adapter = setup_adapter().await;
let pool = adapter.get_pool();
2026-01-26 15:00:52 -08:00
let new_user = User::create(pool, USERNAME).await.unwrap();
2026-01-21 12:47:47 -08:00
let fetched_user = User::get(pool, new_user.id).await.unwrap();
2026-01-26 15:00:52 -08:00
assert_eq!(fetched_user.name, USERNAME);
assert_eq!(fetched_user.id.0, 1);
2026-01-21 12:47:47 -08:00
}
2026-01-26 15:00:52 -08:00
2026-01-21 12:47:47 -08:00
#[tokio::test]
2026-01-26 15:00:52 -08:00
async fn get_all() {
let adapter = setup_adapter().await;
let pool = adapter.get_pool();
2026-01-26 15:00:52 -08:00
User::create(pool, USERNAME).await.unwrap();
User::create(pool, USERNAME2).await.unwrap();
2026-01-21 12:47:47 -08:00
2026-01-26 15:00:52 -08:00
let users = User::get_all(pool).await.unwrap();
assert_eq!(users.len(), 2);
assert!(users.iter().any(|u| u.name == USERNAME));
assert!(users.iter().any(|u| u.name == USERNAME2));
2026-01-21 12:47:47 -08:00
}
2026-01-26 15:00:52 -08:00
2026-01-21 12:47:47 -08:00
#[tokio::test]
2026-01-26 15:00:52 -08:00
async fn create_user() {
let adapter = setup_adapter().await;
2026-01-26 15:00:52 -08:00
let pool = adapter.get_pool();
2026-01-21 12:47:47 -08:00
2026-01-26 15:00:52 -08:00
let user = User::create(pool, USERNAME).await.unwrap();
2026-01-21 12:47:47 -08:00
2026-01-26 15:00:52 -08:00
assert_eq!(user.name, USERNAME);
assert_eq!(user.id.0, 1);
2026-01-21 12:47:47 -08:00
}
2026-01-26 15:00:52 -08:00
2026-01-21 12:47:47 -08:00
#[tokio::test]
2026-01-26 15:00:52 -08:00
async fn create_duplicate_user() {
let adapter = setup_adapter().await;
2026-01-26 15:00:52 -08:00
let pool = adapter.get_pool();
2026-01-21 12:47:47 -08:00
2026-01-26 15:00:52 -08:00
User::create(pool, USERNAME).await.unwrap();
let duplicate_user = User::create(pool, USERNAME).await;
2026-01-21 12:47:47 -08:00
2026-01-26 15:00:52 -08:00
assert!(duplicate_user.is_err());
2026-01-21 12:47:47 -08:00
}
#[tokio::test]
async fn update_name() {
2026-01-26 15:00:52 -08:00
const NEW_USERNAME: &str = "Alicia";
assert!(NEW_USERNAME != USERNAME);
let adapter = setup_adapter().await;
2026-01-26 15:00:52 -08:00
let pool = adapter.get_pool();
2026-01-21 12:47:47 -08:00
2026-01-26 15:00:52 -08:00
let user = User::create(pool, USERNAME).await.unwrap();
User::update_name(pool, user.id, NEW_USERNAME).await.unwrap();
2026-01-21 12:47:47 -08:00
let updated = User::get(pool, user.id).await.unwrap();
2026-01-26 15:00:52 -08:00
assert_eq!(updated.name, NEW_USERNAME);
2026-01-21 12:47:47 -08:00
}
2026-01-26 15:00:52 -08:00
2026-01-21 12:47:47 -08:00
#[tokio::test]
async fn update_name_to_duplicate() {
let adapter = setup_adapter().await;
2026-01-26 15:00:52 -08:00
let pool = adapter.get_pool();
2026-01-21 12:47:47 -08:00
2026-01-26 15:00:52 -08:00
let user1 = User::create(pool, USERNAME).await.unwrap();
User::create(pool, USERNAME2).await.unwrap();
let status = User::update_name(pool, user1.id, USERNAME2).await;
2026-01-21 12:47:47 -08:00
assert!(status.is_err());
}
#[tokio::test]
2026-01-26 15:00:52 -08:00
async fn get_feeds() {
let adapter = setup_adapter().await;
2026-01-26 15:00:52 -08:00
let pool = adapter.get_pool();
let user = User::create(pool, USERNAME).await.unwrap();
Feed::create(pool, user.id, FEED_TITLE).await.unwrap();
Feed::create(pool, user.id, FEED_TITLE2).await.unwrap();
2026-01-21 12:47:47 -08:00
2026-01-26 15:00:52 -08:00
let feeds = user.get_feeds(pool).await.unwrap();
assert_eq!(feeds.len(), 2);
assert!(feeds.iter().any(|f| f.title() == FEED_TITLE));
assert!(feeds.iter().any(|f| f.title() == FEED_TITLE2));
}
#[tokio::test]
async fn get_feeds_empty() {
let adapter = setup_adapter().await;
let pool = adapter.get_pool();
let user = User::create(pool, USERNAME).await.unwrap();
let feeds = user.get_feeds(pool).await.unwrap();
2026-01-21 12:47:47 -08:00
assert_eq!(feeds.len(), 0);
}
2026-01-20 16:08:36 -08:00
}