Finalizing type schema and user tests
This commit is contained in:
parent
162ba78430
commit
e07a98ddbb
2 changed files with 143 additions and 28 deletions
|
|
@ -10,41 +10,42 @@ use crate::{
|
||||||
};
|
};
|
||||||
use sqlx::SqlitePool;
|
use sqlx::SqlitePool;
|
||||||
|
|
||||||
pub struct FeedId(pub i64);
|
#[derive(Copy, Clone)]
|
||||||
impl From<i64> for FeedId { fn from(id: i64) -> Self { FeedId(id) } }
|
pub struct FeedId(i64);
|
||||||
impl From<FeedId> for i64 { fn from(id: FeedId) -> Self { id.0 } }
|
impl From<FeedId> for i64 { fn from(id: FeedId) -> Self { id.0 } }
|
||||||
|
|
||||||
pub struct UnparsedFeed {
|
pub struct UnparsedFeed {
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub user_id: i64
|
|
||||||
}
|
}
|
||||||
impl UnparsedFeed {
|
impl UnparsedFeed {
|
||||||
pub fn parse(self) -> Result<Feed> {
|
pub fn parse(self) -> Result<Feed> {
|
||||||
Ok(Feed {
|
Ok(Feed {
|
||||||
id: FeedId(self.id),
|
id: FeedId(self.id),
|
||||||
title: self.title,
|
title: self.title,
|
||||||
user_id: UserId(self.user_id),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Feed {
|
pub struct Feed {
|
||||||
pub id: FeedId,
|
id: FeedId,
|
||||||
pub title: String,
|
title: String,
|
||||||
pub user_id: UserId,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Feed {
|
impl Feed {
|
||||||
|
pub fn id(&self) -> FeedId { self.id }
|
||||||
|
pub fn title(&self) -> &str { &self.title }
|
||||||
|
|
||||||
pub async fn create(
|
pub async fn create(
|
||||||
pool: &SqlitePool, id: UserId, name: &str
|
pool: &SqlitePool, id: UserId, name: &str
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
|
let int_id = i64::from(id);
|
||||||
let new_feed = sqlx::query_as!(
|
let new_feed = sqlx::query_as!(
|
||||||
UnparsedFeed,
|
UnparsedFeed,
|
||||||
"INSERT INTO feeds (user_id, title)
|
"INSERT INTO feeds (user_id, title)
|
||||||
VALUES (?, ?)
|
VALUES (?, ?)
|
||||||
RETURNING id as `id!`, user_id, title",
|
RETURNING id as `id!`, title",
|
||||||
id.0, name
|
int_id, name
|
||||||
).fetch_one(pool).await?.parse();
|
).fetch_one(pool).await?.parse();
|
||||||
|
|
||||||
new_feed
|
new_feed
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,52 @@
|
||||||
use sqlx::SqlitePool;
|
use sqlx::SqlitePool;
|
||||||
use crate::{
|
use crate::{
|
||||||
Result,
|
Result,
|
||||||
Feed
|
Feed,
|
||||||
|
feed::UnparsedFeed,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct UserId(pub i64);
|
#[derive(Copy, Clone)]
|
||||||
impl From<i64> for UserId { fn from(id: i64) -> Self { UserId(id) } }
|
pub struct UserId(i64);
|
||||||
impl From<UserId> for i64 { fn from(id: UserId) -> Self { id.0 } }
|
impl From<UserId> for i64 { fn from(id: UserId) -> Self { id.0 } }
|
||||||
|
|
||||||
pub struct User {
|
pub struct UnparsedUser {
|
||||||
pub id: UserId,
|
pub id: i64,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
impl UnparsedUser {
|
||||||
|
pub fn parse(self) -> Result<User> {
|
||||||
|
Ok(User {
|
||||||
|
id: UserId(self.id),
|
||||||
|
name: self.name
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct User {
|
||||||
|
id: UserId,
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
impl User {
|
impl User {
|
||||||
pub async fn get_all(pool: &SqlitePool) -> Result<Vec<Self>> {
|
pub fn id(&self) -> UserId { self.id }
|
||||||
let users = sqlx::query_as!(
|
pub fn name(&self) -> &str { &self.name }
|
||||||
User,
|
|
||||||
"SELECT id, name FROM users"
|
|
||||||
).fetch_all(pool).await?;
|
|
||||||
|
|
||||||
Ok(users)
|
pub async fn get(pool: &SqlitePool, id: UserId) -> Result<Self> {
|
||||||
|
let user = sqlx::query_as!(
|
||||||
|
UnparsedUser,
|
||||||
|
"SELECT id, name FROM users WHERE id = ?",
|
||||||
|
id.0
|
||||||
|
).fetch_one(pool).await?.parse();
|
||||||
|
|
||||||
|
user
|
||||||
|
}
|
||||||
|
pub async fn get_all(pool: &SqlitePool) -> Result<Vec<Self>> {
|
||||||
|
let users: Result<Vec<Self>> = sqlx::query_as!(
|
||||||
|
UnparsedUser,
|
||||||
|
"SELECT id, name FROM users"
|
||||||
|
).fetch_all(pool).await?.into_iter().map(UnparsedUser::parse).collect();
|
||||||
|
|
||||||
|
users
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create(pool: &SqlitePool, name: &str) -> Result<Self> {
|
pub async fn create(pool: &SqlitePool, name: &str) -> Result<Self> {
|
||||||
|
|
@ -48,15 +74,103 @@ impl User {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_feeds(pool: &SqlitePool, id: UserId) -> Result<Vec<Feed>> {
|
pub async fn get_feeds(&self, pool: &SqlitePool) -> Result<Vec<Feed>> {
|
||||||
let feeds = sqlx::query_as!(
|
let feeds: Result<Vec<Feed>> = sqlx::query_as!(
|
||||||
Feed,
|
UnparsedFeed,
|
||||||
"SELECT id FROM feeds WHERE user_id = ?",
|
"SELECT id, title FROM feeds WHERE user_id = ?",
|
||||||
id.0
|
self.id.0
|
||||||
).fetch_all(pool).await?;
|
).fetch_all(pool).await?.into_iter()
|
||||||
|
.map(UnparsedFeed::parse).collect();
|
||||||
|
|
||||||
Ok(feeds)
|
feeds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use sqlx::SqlitePool;
|
||||||
|
|
||||||
|
async fn setup_test_db() -> SqlitePool {
|
||||||
|
let pool = SqlitePool::connect("sqlite::memory:").await.unwrap();
|
||||||
|
sqlx::migrate!().run(&pool).await.unwrap();
|
||||||
|
pool
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn get_user() {
|
||||||
|
let pool = setup_test_db().await;
|
||||||
|
|
||||||
|
let new_user = User::create(&pool, "Alice").await.unwrap();
|
||||||
|
|
||||||
|
let fetched_user = User::get(&pool, new_user.id).await.unwrap();
|
||||||
|
assert_eq!(fetched_user.name, "Alice");
|
||||||
|
assert!(fetched_user.id.0 > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn name(&self) -> &str { &self.name }
|
#[tokio::test]
|
||||||
|
async fn create_user() {
|
||||||
|
let pool = setup_test_db().await;
|
||||||
|
|
||||||
|
let user = User::create(&pool, "Alice").await.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(user.name, "Alice");
|
||||||
|
assert!(user.id.0 > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn create_duplicate_user() {
|
||||||
|
let pool = setup_test_db().await;
|
||||||
|
|
||||||
|
let _user = User::create(&pool, "Alice").await.unwrap();
|
||||||
|
let duplicate_user = User::create(&pool, "Alice").await;
|
||||||
|
|
||||||
|
assert!(duplicate_user.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn get_all_users() {
|
||||||
|
let pool = setup_test_db().await;
|
||||||
|
|
||||||
|
User::create(&pool, "Alice").await.unwrap();
|
||||||
|
User::create(&pool, "Bob").await.unwrap();
|
||||||
|
|
||||||
|
let users = User::get_all(&pool).await.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(users.len(), 2);
|
||||||
|
assert!(users.iter().any(|u| u.name == "Alice"));
|
||||||
|
assert!(users.iter().any(|u| u.name == "Bob"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn update_name() {
|
||||||
|
let pool = setup_test_db().await;
|
||||||
|
|
||||||
|
let user = User::create(&pool, "Alice").await.unwrap();
|
||||||
|
User::update_name(&pool, user.id, "Alicia").await.unwrap();
|
||||||
|
|
||||||
|
let updated = User::get(&pool, user.id).await.unwrap();
|
||||||
|
assert_eq!(updated.name, "Alicia");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn update_name_to_duplicate() {
|
||||||
|
let pool = setup_test_db().await;
|
||||||
|
|
||||||
|
let alice = User::create(&pool, "Alice").await.unwrap();
|
||||||
|
let _sam = User::create(&pool, "Sam").await.unwrap();
|
||||||
|
let status = User::update_name(&pool, alice.id, "Sam").await;
|
||||||
|
|
||||||
|
assert!(status.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn get_feeds_empty() {
|
||||||
|
let pool = setup_test_db().await;
|
||||||
|
|
||||||
|
let user = User::create(&pool, "Alice").await.unwrap();
|
||||||
|
let feeds = user.get_feeds(&pool).await.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(feeds.len(), 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue