(Almost) full test coverage
This commit is contained in:
parent
0639c5ca12
commit
544e380835
8 changed files with 417 additions and 100 deletions
|
|
@ -160,19 +160,71 @@ impl Channel {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{Adapter, AdapterBuilder};
|
use crate::{
|
||||||
|
db::{
|
||||||
|
Feed,
|
||||||
|
User,
|
||||||
|
},
|
||||||
|
test_utils::{
|
||||||
|
FEED1, FEED2, CHANNEL_TITLE, CHANNEL_DESC, USERNAME, FEED_TITLE,
|
||||||
|
FEED_TITLE2, ITEM_GUID, ITEM_GUID2,
|
||||||
|
setup_adapter,
|
||||||
|
setup_channel,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use chrono::TimeZone;
|
||||||
|
|
||||||
const FEED1: &str = "https://example.com/feed";
|
#[test]
|
||||||
const FEED2: &str = "https://example2.com/feed";
|
fn parse_unparsed_item() {
|
||||||
|
const CHANNEL_ID: i64 = 1;
|
||||||
|
let date: DateTime<Utc> = Utc.with_ymd_and_hms(2020,1,1,0,0,0).unwrap();
|
||||||
|
|
||||||
async fn setup_adapter() -> Adapter {
|
let raw_channel = UnparsedChannel {
|
||||||
AdapterBuilder::new()
|
id: CHANNEL_ID,
|
||||||
.database_url("sqlite::memory:")
|
title: CHANNEL_TITLE.to_string(),
|
||||||
.create().await.unwrap()
|
link: FEED1.to_string(),
|
||||||
|
description: Some(CHANNEL_DESC.to_string()),
|
||||||
|
last_fetched: Some(date.to_rfc2822()),
|
||||||
|
};
|
||||||
|
let channel = raw_channel.parse().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(channel.id.0, CHANNEL_ID);
|
||||||
|
assert_eq!(channel.title, CHANNEL_TITLE);
|
||||||
|
assert_eq!(channel.link.as_str(), FEED1);
|
||||||
|
assert_eq!(channel.description, Some(CHANNEL_DESC.to_string()));
|
||||||
|
assert_eq!(channel.last_fetched, Some(date));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn create_channel() {
|
async fn get_all() {
|
||||||
|
let adapter = setup_adapter().await;
|
||||||
|
let pool = adapter.get_pool();
|
||||||
|
let url1 = Url::parse(FEED1).unwrap();
|
||||||
|
let url2 = Url::parse(FEED2).unwrap();
|
||||||
|
Channel::get_or_create(pool, url1).await.unwrap();
|
||||||
|
Channel::get_or_create(pool, url2).await.unwrap();
|
||||||
|
|
||||||
|
let channels = Channel::get_all(pool).await.unwrap();
|
||||||
|
assert_eq!(channels.len(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn get() {
|
||||||
|
let adapter = setup_adapter().await;
|
||||||
|
let pool = adapter.get_pool();
|
||||||
|
let channel_a = setup_channel(pool).await;
|
||||||
|
|
||||||
|
let channel_b = Channel::get(pool, channel_a.id()).await.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(channel_a.id, channel_b.id);
|
||||||
|
assert_eq!(channel_a.title, channel_b.title);
|
||||||
|
assert_eq!(channel_a.link, channel_b.link);
|
||||||
|
assert_eq!(channel_a.last_fetched, channel_b.last_fetched);
|
||||||
|
assert_eq!(channel_a.description, channel_b.description);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn create() {
|
||||||
let adapter = setup_adapter().await;
|
let adapter = setup_adapter().await;
|
||||||
let pool = adapter.get_pool();
|
let pool = adapter.get_pool();
|
||||||
let url_feed = Url::parse(FEED1).unwrap();
|
let url_feed = Url::parse(FEED1).unwrap();
|
||||||
|
|
@ -213,4 +265,54 @@ mod tests {
|
||||||
|
|
||||||
assert_eq!(channels.len(), 2);
|
assert_eq!(channels.len(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn get_feed_channels() {
|
||||||
|
let adapter = setup_adapter().await;
|
||||||
|
let pool = adapter.get_pool();
|
||||||
|
let channel = setup_channel(pool).await;
|
||||||
|
|
||||||
|
let user = User::create(pool, USERNAME).await.unwrap();
|
||||||
|
let feed1 = Feed::create(pool, user.id(), FEED_TITLE).await.unwrap();
|
||||||
|
let feed2 = Feed::create(pool, user.id(), FEED_TITLE2).await.unwrap();
|
||||||
|
|
||||||
|
feed1.add_channel(pool, channel.id).await.unwrap();
|
||||||
|
feed2.add_channel(pool, channel.id).await.unwrap();
|
||||||
|
|
||||||
|
let fc_list = channel.get_feed_channels(pool).await.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(fc_list.len(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn get_channels() {
|
||||||
|
let adapter = setup_adapter().await;
|
||||||
|
let pool = adapter.get_pool();
|
||||||
|
let channel = setup_channel(pool).await;
|
||||||
|
|
||||||
|
let user = User::create(pool, USERNAME).await.unwrap();
|
||||||
|
let feed1 = Feed::create(pool, user.id(), FEED_TITLE).await.unwrap();
|
||||||
|
let feed2 = Feed::create(pool, user.id(), FEED_TITLE2).await.unwrap();
|
||||||
|
|
||||||
|
feed1.add_channel(pool, channel.id).await.unwrap();
|
||||||
|
feed2.add_channel(pool, channel.id).await.unwrap();
|
||||||
|
|
||||||
|
let fc_list = channel.get_feed_channels(pool).await.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(fc_list.len(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn get_items() {
|
||||||
|
let adapter = setup_adapter().await;
|
||||||
|
let pool = adapter.get_pool();
|
||||||
|
let channel = setup_channel(pool).await;
|
||||||
|
|
||||||
|
Item::get_or_create(pool, channel.id(), ITEM_GUID).await.unwrap();
|
||||||
|
Item::get_or_create(pool, channel.id(), ITEM_GUID2).await.unwrap();
|
||||||
|
|
||||||
|
let items = channel.get_items(pool).await.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(items.len(), 2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -124,39 +124,96 @@ impl Feed {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
Adapter,
|
|
||||||
AdapterBuilder,
|
|
||||||
db::User,
|
db::User,
|
||||||
|
test_utils::{
|
||||||
|
FEED_TITLE, USERNAME, FEED1, FEED2,
|
||||||
|
setup_adapter,
|
||||||
|
setup_feed,
|
||||||
|
setup_channel,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
use reqwest::Url;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse() {
|
||||||
|
const FID: i64 = 1;
|
||||||
|
let uf = UnparsedFeed {
|
||||||
|
id: FID,
|
||||||
|
title: FEED_TITLE.to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
async fn setup_adapter() -> Adapter {
|
let f = uf.parse().unwrap();
|
||||||
AdapterBuilder::new()
|
|
||||||
.database_url("sqlite::memory:")
|
assert_eq!(f.id.0, FID);
|
||||||
.create().await.unwrap()
|
assert_eq!(f.title, FEED_TITLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn create_feed() {
|
async fn get() {
|
||||||
let adapter = setup_adapter().await;
|
let adapter = setup_adapter().await;
|
||||||
let pool = adapter.get_pool();
|
let pool = adapter.get_pool();
|
||||||
let user = User::create(pool, "Alice").await.unwrap();
|
let feed = setup_feed(pool).await;
|
||||||
|
|
||||||
let feed = Feed::create(pool, user.id(), "Tech News").await.unwrap();
|
let gotten_feed = Feed::get(pool, feed.id).await.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(feed.id, gotten_feed.id);
|
||||||
|
assert_eq!(feed.title, gotten_feed.title);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn create() {
|
||||||
|
let adapter = setup_adapter().await;
|
||||||
|
let pool = adapter.get_pool();
|
||||||
|
let user = User::create(pool, USERNAME).await.unwrap();
|
||||||
|
let feed = Feed::create(pool, user.id(), FEED_TITLE).await.unwrap();
|
||||||
|
|
||||||
assert_eq!(feed.title(), "Tech News");
|
|
||||||
assert!(feed.id().0 > 0);
|
assert!(feed.id().0 > 0);
|
||||||
|
assert_eq!(feed.title(), FEED_TITLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_update_title() {
|
async fn update_title() {
|
||||||
|
const NEW_FEED_TITLE: &str = "My NEW feed!";
|
||||||
let adapter = setup_adapter().await;
|
let adapter = setup_adapter().await;
|
||||||
let pool = adapter.get_pool();
|
let pool = adapter.get_pool();
|
||||||
let user = User::create(pool, "Alice").await.unwrap();
|
let feed = setup_feed(pool).await;
|
||||||
let feed = Feed::create(pool, user.id(), "Tech News").await.unwrap();
|
|
||||||
|
|
||||||
Feed::update_title(pool, feed.id(), "Technology").await.unwrap();
|
Feed::update_title(pool, feed.id(), NEW_FEED_TITLE).await.unwrap();
|
||||||
|
|
||||||
let updated = Feed::get(pool, feed.id()).await.unwrap();
|
let updated = Feed::get(pool, feed.id()).await.unwrap();
|
||||||
assert_eq!(updated.title(), "Technology");
|
assert_eq!(updated.title(), NEW_FEED_TITLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn add_channel() {
|
||||||
|
let adapter = setup_adapter().await;
|
||||||
|
let pool = adapter.get_pool();
|
||||||
|
let feed = setup_feed(pool).await;
|
||||||
|
let channel = setup_channel(pool).await;
|
||||||
|
|
||||||
|
feed.add_channel(pool, channel.id()).await.unwrap();
|
||||||
|
|
||||||
|
let channels = feed.get_channels(pool).await.unwrap();
|
||||||
|
let gotten_channel = &channels[0];
|
||||||
|
|
||||||
|
assert_eq!(gotten_channel.id().0, channel.id().0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn get_channels() {
|
||||||
|
let adapter = setup_adapter().await;
|
||||||
|
let pool = adapter.get_pool();
|
||||||
|
let feed = setup_feed(pool).await;
|
||||||
|
let url1 = Url::parse(FEED1).unwrap();
|
||||||
|
let channel1 = Channel::get_or_create(pool, url1).await.unwrap();
|
||||||
|
let url2 = Url::parse(FEED2).unwrap();
|
||||||
|
let channel2 = Channel::get_or_create(pool, url2).await.unwrap();
|
||||||
|
|
||||||
|
feed.add_channel(pool, channel1.id()).await.unwrap();
|
||||||
|
feed.add_channel(pool, channel2.id()).await.unwrap();
|
||||||
|
|
||||||
|
let channels = feed.get_channels(pool).await.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(channels.len(), 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,21 +56,29 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use reqwest::Url;
|
use reqwest::Url;
|
||||||
use crate::{
|
use crate::{
|
||||||
Adapter,
|
|
||||||
AdapterBuilder,
|
|
||||||
db::{
|
db::{
|
||||||
Channel,
|
Channel,
|
||||||
FeedId,
|
FeedId,
|
||||||
User
|
User
|
||||||
}
|
},
|
||||||
|
test_utils::{
|
||||||
|
FEED1, setup_adapter,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const FEED1: &str = "https://example.com/feed";
|
#[test]
|
||||||
|
fn parse() {
|
||||||
|
const CID: i64 = 1;
|
||||||
|
const FID: i64 = 1;
|
||||||
|
let ufc = UnparsedFeedChannel {
|
||||||
|
channel_id: CID,
|
||||||
|
feed_id: FID,
|
||||||
|
};
|
||||||
|
|
||||||
async fn setup_adapter() -> Adapter {
|
let fc = ufc.parse().unwrap();
|
||||||
AdapterBuilder::new()
|
|
||||||
.database_url("sqlite::memory:")
|
assert_eq!(fc.channel_id.0, CID);
|
||||||
.create().await.unwrap()
|
assert_eq!(fc.feed_id.0, FID);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FeedChannel Tests
|
// FeedChannel Tests
|
||||||
|
|
|
||||||
|
|
@ -97,32 +97,42 @@ impl Item {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use reqwest::Url;
|
use crate::test_utils::{
|
||||||
use crate::{
|
ITEM_GUID, ITEM_TITLE, ITEM_DESC, ITEM_CONT,
|
||||||
Adapter,
|
setup_adapter,
|
||||||
AdapterBuilder,
|
setup_channel,
|
||||||
db::{
|
|
||||||
Channel,
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
use chrono::TimeZone;
|
||||||
|
|
||||||
const FEED1: &str = "https://example.com/feed";
|
// UnparsedItem tests
|
||||||
const ITEM_GUID: &str = "item-guid";
|
#[test]
|
||||||
|
fn parse_unparsed_item() {
|
||||||
|
const ITEM_ID: i64 = 1;
|
||||||
|
const CHANNEL_ID: i64 = 1;
|
||||||
|
|
||||||
async fn setup_adapter() -> Adapter {
|
let date: DateTime<Utc> = Utc.with_ymd_and_hms(2020,1,1,0,0,0).unwrap();
|
||||||
AdapterBuilder::new()
|
let raw_item = UnparsedItem {
|
||||||
.database_url("sqlite::memory:")
|
id: ITEM_ID,
|
||||||
.create().await.unwrap()
|
channel_id: CHANNEL_ID,
|
||||||
}
|
fetched_at: Some(date.to_rfc2822()),
|
||||||
|
title: Some(ITEM_TITLE.to_string()),
|
||||||
|
description: Some(ITEM_DESC.to_string()),
|
||||||
|
content: Some(ITEM_CONT.to_string()),
|
||||||
|
};
|
||||||
|
let item = raw_item.parse().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(item.id.0, ITEM_ID);
|
||||||
|
assert_eq!(item.channel_id.0, CHANNEL_ID);
|
||||||
|
assert_eq!(item.fetched_at, Some(date));
|
||||||
|
assert_eq!(item.title, Some(ITEM_TITLE.to_string()));
|
||||||
|
assert_eq!(item.description, Some(ITEM_DESC.to_string()));
|
||||||
|
assert_eq!(item.content, Some(ITEM_CONT.to_string()));
|
||||||
|
|
||||||
async fn setup_channel(pool: &AdapterPool) -> Channel {
|
|
||||||
let url = Url::parse(FEED1).unwrap();
|
|
||||||
Channel::get_or_create(pool, url).await.unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Item Tests
|
// Item Tests
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
pub async fn get_or_create_duplicate() {
|
async fn get_or_create_duplicate() {
|
||||||
let adapter = setup_adapter().await;
|
let adapter = setup_adapter().await;
|
||||||
let pool = adapter.get_pool();
|
let pool = adapter.get_pool();
|
||||||
let channel = setup_channel(pool).await;
|
let channel = setup_channel(pool).await;
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ impl User {
|
||||||
|
|
||||||
user
|
user
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_all(pool: &AdapterPool) -> Result<Vec<Self>> {
|
pub async fn get_all(pool: &AdapterPool) -> Result<Vec<Self>> {
|
||||||
let users: Result<Vec<Self>> = sqlx::query_as!(
|
let users: Result<Vec<Self>> = sqlx::query_as!(
|
||||||
UnparsedUser,
|
UnparsedUser,
|
||||||
|
|
@ -88,24 +89,49 @@ impl User {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{AdapterBuilder, Adapter};
|
use crate::{
|
||||||
|
db::Feed,
|
||||||
|
test_utils::{
|
||||||
|
USERNAME, USERNAME2, FEED_TITLE, FEED_TITLE2,
|
||||||
|
setup_adapter,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
async fn setup_adapter() -> Adapter {
|
#[test]
|
||||||
AdapterBuilder::new()
|
fn parse() {
|
||||||
.database_url("sqlite::memory:")
|
const UID: i64 = 1;
|
||||||
.create().await.unwrap()
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn get_user() {
|
async fn get() {
|
||||||
let adapter = setup_adapter().await;
|
let adapter = setup_adapter().await;
|
||||||
let pool = adapter.get_pool();
|
let pool = adapter.get_pool();
|
||||||
|
let new_user = User::create(pool, USERNAME).await.unwrap();
|
||||||
let new_user = User::create(pool, "Alice").await.unwrap();
|
|
||||||
|
|
||||||
let fetched_user = User::get(pool, new_user.id).await.unwrap();
|
let fetched_user = User::get(pool, new_user.id).await.unwrap();
|
||||||
assert_eq!(fetched_user.name, "Alice");
|
assert_eq!(fetched_user.name, USERNAME);
|
||||||
assert!(fetched_user.id.0 > 0);
|
assert_eq!(fetched_user.id.0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn get_all() {
|
||||||
|
let adapter = setup_adapter().await;
|
||||||
|
let pool = adapter.get_pool();
|
||||||
|
User::create(pool, USERNAME).await.unwrap();
|
||||||
|
User::create(pool, USERNAME2).await.unwrap();
|
||||||
|
|
||||||
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
|
@ -113,10 +139,10 @@ mod tests {
|
||||||
let adapter = setup_adapter().await;
|
let adapter = setup_adapter().await;
|
||||||
let pool = adapter.get_pool();
|
let pool = adapter.get_pool();
|
||||||
|
|
||||||
let user = User::create(pool, "Alice").await.unwrap();
|
let user = User::create(pool, USERNAME).await.unwrap();
|
||||||
|
|
||||||
assert_eq!(user.name, "Alice");
|
assert_eq!(user.name, USERNAME);
|
||||||
assert!(user.id.0 > 0);
|
assert_eq!(user.id.0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
|
@ -124,37 +150,24 @@ mod tests {
|
||||||
let adapter = setup_adapter().await;
|
let adapter = setup_adapter().await;
|
||||||
let pool = adapter.get_pool();
|
let pool = adapter.get_pool();
|
||||||
|
|
||||||
let _user = User::create(pool, "Alice").await.unwrap();
|
User::create(pool, USERNAME).await.unwrap();
|
||||||
let duplicate_user = User::create(pool, "Alice").await;
|
let duplicate_user = User::create(pool, USERNAME).await;
|
||||||
|
|
||||||
assert!(duplicate_user.is_err());
|
assert!(duplicate_user.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn get_all_users() {
|
|
||||||
let adapter = setup_adapter().await;
|
|
||||||
let pool = adapter.get_pool();
|
|
||||||
|
|
||||||
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]
|
#[tokio::test]
|
||||||
async fn update_name() {
|
async fn update_name() {
|
||||||
|
const NEW_USERNAME: &str = "Alicia";
|
||||||
|
assert!(NEW_USERNAME != USERNAME);
|
||||||
let adapter = setup_adapter().await;
|
let adapter = setup_adapter().await;
|
||||||
let pool = adapter.get_pool();
|
let pool = adapter.get_pool();
|
||||||
|
|
||||||
let user = User::create(pool, "Alice").await.unwrap();
|
let user = User::create(pool, USERNAME).await.unwrap();
|
||||||
User::update_name(pool, user.id, "Alicia").await.unwrap();
|
User::update_name(pool, user.id, NEW_USERNAME).await.unwrap();
|
||||||
|
|
||||||
let updated = User::get(pool, user.id).await.unwrap();
|
let updated = User::get(pool, user.id).await.unwrap();
|
||||||
assert_eq!(updated.name, "Alicia");
|
assert_eq!(updated.name, NEW_USERNAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
|
@ -162,19 +175,32 @@ mod tests {
|
||||||
let adapter = setup_adapter().await;
|
let adapter = setup_adapter().await;
|
||||||
let pool = adapter.get_pool();
|
let pool = adapter.get_pool();
|
||||||
|
|
||||||
let alice = User::create(pool, "Alice").await.unwrap();
|
let user1 = User::create(pool, USERNAME).await.unwrap();
|
||||||
let _sam = User::create(pool, "Sam").await.unwrap();
|
User::create(pool, USERNAME2).await.unwrap();
|
||||||
let status = User::update_name(pool, alice.id, "Sam").await;
|
let status = User::update_name(pool, user1.id, USERNAME2).await;
|
||||||
|
|
||||||
assert!(status.is_err());
|
assert!(status.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn get_feeds() {
|
||||||
|
let adapter = setup_adapter().await;
|
||||||
|
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();
|
||||||
|
|
||||||
|
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]
|
#[tokio::test]
|
||||||
async fn get_feeds_empty() {
|
async fn get_feeds_empty() {
|
||||||
let adapter = setup_adapter().await;
|
let adapter = setup_adapter().await;
|
||||||
let pool = adapter.get_pool();
|
let pool = adapter.get_pool();
|
||||||
|
let user = User::create(pool, USERNAME).await.unwrap();
|
||||||
let user = User::create(pool, "Alice").await.unwrap();
|
|
||||||
let feeds = user.get_feeds(pool).await.unwrap();
|
let feeds = user.get_feeds(pool).await.unwrap();
|
||||||
|
|
||||||
assert_eq!(feeds.len(), 0);
|
assert_eq!(feeds.len(), 0);
|
||||||
|
|
|
||||||
|
|
@ -70,10 +70,12 @@ impl FetchedRSSChannel {
|
||||||
|
|
||||||
let rss_channel = rss::Channel::read_from(&bytestream[..])?;
|
let rss_channel = rss::Channel::read_from(&bytestream[..])?;
|
||||||
|
|
||||||
Ok(Some(FetchedRSSChannel::parse(rss_channel)?))
|
let now = Utc::now();
|
||||||
|
|
||||||
|
Ok(Some(FetchedRSSChannel::parse(rss_channel, now)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse(rss: rss::Channel) -> Result<Self> {
|
fn parse(rss: rss::Channel, fetched_at: DateTime<Utc>) -> Result<Self> {
|
||||||
Ok(FetchedRSSChannel {
|
Ok(FetchedRSSChannel {
|
||||||
title: rss.title,
|
title: rss.title,
|
||||||
link: Url::parse(&rss.link)?,
|
link: Url::parse(&rss.link)?,
|
||||||
|
|
@ -81,7 +83,73 @@ impl FetchedRSSChannel {
|
||||||
|
|
||||||
items: rss.items.into_iter().map(FetchedRSSItem::parse).collect(),
|
items: rss.items.into_iter().map(FetchedRSSItem::parse).collect(),
|
||||||
|
|
||||||
fetched_at: Utc::now(),
|
fetched_at: fetched_at,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::test_utils::{
|
||||||
|
ITEM_TITLE, ITEM_GUID, ITEM_GUID2, ITEM_DESC, ITEM_CONT,
|
||||||
|
CHANNEL_TITLE, CHANNEL_DESC, FEED1
|
||||||
|
};
|
||||||
|
use chrono::TimeZone;
|
||||||
|
|
||||||
|
fn create_guid(value: String) -> rss::Guid {
|
||||||
|
rss::Guid { value, permalink: false }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_item(guid: rss::Guid) -> rss::Item {
|
||||||
|
rss::ItemBuilder::default()
|
||||||
|
.title(ITEM_TITLE.to_string())
|
||||||
|
.guid(guid)
|
||||||
|
.description(ITEM_DESC.to_string())
|
||||||
|
.content(ITEM_CONT.to_string())
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_channel(items: Vec<rss::Item>) -> rss::Channel {
|
||||||
|
rss::ChannelBuilder::default()
|
||||||
|
.title(CHANNEL_TITLE.to_string())
|
||||||
|
.description(CHANNEL_DESC.to_string())
|
||||||
|
.link(FEED1.to_string())
|
||||||
|
.items(items)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_item() {
|
||||||
|
let rss_guid = create_guid(ITEM_GUID.to_string());
|
||||||
|
let rss_item = create_item(rss_guid);
|
||||||
|
let item = FetchedRSSItem::parse(rss_item);
|
||||||
|
|
||||||
|
assert_eq!(item.guid, ITEM_GUID);
|
||||||
|
assert_eq!(item.title, ITEM_TITLE);
|
||||||
|
assert_eq!(item.description, ITEM_DESC);
|
||||||
|
assert_eq!(item.content, ITEM_CONT);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_feed() {
|
||||||
|
let rss_guid = create_guid(ITEM_GUID.to_string());
|
||||||
|
let rss_guid2 = create_guid(ITEM_GUID2.to_string());
|
||||||
|
let rss_item = create_item(rss_guid);
|
||||||
|
let rss_item2 = create_item(rss_guid2);
|
||||||
|
|
||||||
|
let rss_channel = create_channel([rss_item, rss_item2].to_vec());
|
||||||
|
|
||||||
|
let date: DateTime<Utc> = Utc.with_ymd_and_hms(2020,1,1,0,0,0).unwrap();
|
||||||
|
|
||||||
|
let channel = FetchedRSSChannel::parse(rss_channel, date).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(channel.title, CHANNEL_TITLE);
|
||||||
|
assert_eq!(channel.link.as_str(), FEED1);
|
||||||
|
assert_eq!(channel.description, CHANNEL_DESC);
|
||||||
|
assert_eq!(channel.fetched_at, date);
|
||||||
|
assert_eq!(channel.items.len(), 2);
|
||||||
|
assert!(channel.items.iter().any(|i| i.guid() == ITEM_GUID));
|
||||||
|
assert!(channel.items.iter().any(|i| i.guid() == ITEM_GUID2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@ type Result<T> = std::result::Result<T, Box<dyn Error>>;
|
||||||
pub mod db;
|
pub mod db;
|
||||||
pub mod fetch;
|
pub mod fetch;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod test_utils;
|
||||||
|
|
||||||
pub struct AdapterPool(sqlx::SqlitePool);
|
pub struct AdapterPool(sqlx::SqlitePool);
|
||||||
pub struct AdapterClient(reqwest::Client);
|
pub struct AdapterClient(reqwest::Client);
|
||||||
|
|
||||||
|
|
|
||||||
43
koucha/src/test_utils.rs
Normal file
43
koucha/src/test_utils.rs
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
#![cfg(test)]
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
Adapter,
|
||||||
|
AdapterBuilder,
|
||||||
|
AdapterPool,
|
||||||
|
db::{
|
||||||
|
Channel,
|
||||||
|
Feed,
|
||||||
|
User,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
use reqwest::Url;
|
||||||
|
|
||||||
|
pub const FEED1: &str = "https://example.com/feed";
|
||||||
|
pub const FEED2: &str = "https://example2.com/feed";
|
||||||
|
pub const USERNAME: &str = "Alice";
|
||||||
|
pub const USERNAME2: &str = "Bob";
|
||||||
|
pub const FEED_TITLE: &str = "My Feed!";
|
||||||
|
pub const FEED_TITLE2: &str = "My Second Feed!";
|
||||||
|
pub const CHANNEL_TITLE: &str = "My Channel!";
|
||||||
|
pub const CHANNEL_DESC: &str = "My Channel's description";
|
||||||
|
pub const ITEM_GUID: &str = "item-guid";
|
||||||
|
pub const ITEM_GUID2: &str = "item-guid2";
|
||||||
|
pub const ITEM_TITLE: &str = "My Item!";
|
||||||
|
pub const ITEM_DESC: &str = "My Item's description";
|
||||||
|
pub const ITEM_CONT: &str = "The content of my Item";
|
||||||
|
|
||||||
|
pub async fn setup_adapter() -> Adapter {
|
||||||
|
AdapterBuilder::new()
|
||||||
|
.database_url("sqlite::memory:")
|
||||||
|
.create().await.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn setup_channel(pool: &AdapterPool) -> Channel {
|
||||||
|
let url = Url::parse(FEED1).unwrap();
|
||||||
|
Channel::get_or_create(pool, url).await.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn setup_feed(pool: &AdapterPool) -> Feed {
|
||||||
|
let user = User::create(pool, USERNAME).await.unwrap();
|
||||||
|
Feed::create(pool, user.id(), FEED_TITLE).await.unwrap()
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue