use super::*;
mod basic_operations {
use super::*;
#[tokio::test]
#[traced_test]
async fn test_remove_existing_paper() -> TestResult<()> {
let (mut learner, _cfg_dir, _db_dir, _strg_dir) = create_test_learner().await;
let paper = create_test_paper();
Add::paper(&paper).execute(&mut learner.database).await?;
let removed_papers = Remove::by_source(&paper.source, &paper.source_identifier)
.execute(&mut learner.database)
.await?;
assert_eq!(removed_papers.len(), 1);
assert_eq!(removed_papers[0].title, paper.title);
assert_eq!(removed_papers[0].authors.len(), paper.authors.len());
let results = Query::by_source(&paper.source, &paper.source_identifier)
.execute(&mut learner.database)
.await?;
assert_eq!(results.len(), 0);
Ok(())
}
#[tokio::test]
#[traced_test]
async fn test_remove_nonexistent_paper() -> TestResult<()> {
let (mut learner, _cfg_dir, _db_dir, _strg_dir) = create_test_learner().await;
let removed = Remove::by_source("arxiv", "nonexistent").execute(&mut learner.database).await?;
assert!(removed.is_empty());
Ok(())
}
#[tokio::test]
#[traced_test]
async fn test_remove_cascades_to_authors() -> TestResult<()> {
let (mut learner, _cfg_dir, _db_dir, _strg_dir) = create_test_learner().await;
let paper = create_test_paper();
Add::paper(&paper).execute(&mut learner.database).await?;
Remove::from_query(Query::text("test")).execute(&mut learner.database).await?;
let authors = Query::by_author("").execute(&mut learner.database).await?;
assert_eq!(authors.len(), 0);
Ok(())
}
#[tokio::test]
#[traced_test]
async fn test_remove_complete_paper() -> TestResult<()> {
let (mut learner, _cfg_dir, _db_dir, _strg_dir) = create_test_learner().await;
let paper = learner.retriever.get_paper("https://arxiv.org/abs/2301.07041").await?;
Add::complete(&paper).execute(&mut learner.database).await?;
Remove::by_source(&paper.source, &paper.source_identifier)
.execute(&mut learner.database)
.await?;
let results = Query::by_source(&paper.source, &paper.source_identifier)
.execute(&mut learner.database)
.await?;
assert_eq!(results.len(), 0);
Ok(())
}
}
mod dry_run {
use super::*;
#[tokio::test]
#[traced_test]
async fn test_dry_run_basic() -> TestResult<()> {
let (mut learner, _cfg_dir, _db_dir, _strg_dir) = create_test_learner().await;
let paper = create_test_paper();
Add::paper(&paper).execute(&mut learner.database).await?;
let would_remove = Remove::by_source(&paper.source, &paper.source_identifier)
.dry_run()
.execute(&mut learner.database)
.await?;
assert_eq!(would_remove.len(), 1);
assert_eq!(would_remove[0].title, paper.title);
let results = Query::by_source(&paper.source, &paper.source_identifier)
.execute(&mut learner.database)
.await?;
assert_eq!(results.len(), 1);
Ok(())
}
#[tokio::test]
#[traced_test]
async fn test_dry_run_returns_complete_paper() -> TestResult<()> {
let (mut learner, _cfg_dir, _db_dir, _strg_dir) = create_test_learner().await;
let paper = create_test_paper();
Add::paper(&paper).execute(&mut learner.database).await?;
let would_remove =
Remove::from_query(Query::text("test")).dry_run().execute(&mut learner.database).await?;
assert_eq!(would_remove.len(), 1);
let removed = &would_remove[0];
assert_eq!(removed.title, paper.title);
assert_eq!(removed.abstract_text, paper.abstract_text);
assert_eq!(removed.publication_date, paper.publication_date);
assert_eq!(removed.source, paper.source);
assert_eq!(removed.source_identifier, paper.source_identifier);
assert_eq!(removed.pdf_url, paper.pdf_url);
assert_eq!(removed.doi, paper.doi);
assert_eq!(removed.authors.len(), paper.authors.len());
for (removed_author, original_author) in removed.authors.iter().zip(paper.authors.iter()) {
assert_eq!(removed_author.name, original_author.name);
assert_eq!(removed_author.affiliation, original_author.affiliation);
assert_eq!(removed_author.email, original_author.email);
}
Ok(())
}
#[tokio::test]
#[traced_test]
async fn test_dry_run_with_complete_paper() -> TestResult<()> {
let (mut learner, _cfg_dir, _db_dir, _strg_dir) = create_test_learner().await;
let paper = learner.retriever.get_paper("https://arxiv.org/abs/2301.07041").await?;
Add::complete(&paper).execute(&mut learner.database).await?;
let would_remove = Remove::by_source(&paper.source, &paper.source_identifier)
.dry_run()
.execute(&mut learner.database)
.await?;
assert_eq!(would_remove.len(), 1);
let results = Query::by_source(&paper.source, &paper.source_identifier)
.execute(&mut learner.database)
.await?;
assert_eq!(results.len(), 1);
Ok(())
}
}
mod query_based_removal {
use super::*;
#[tokio::test]
#[traced_test]
async fn test_remove_by_text_search() -> TestResult<()> {
let (mut learner, _cfg_dir, _db_dir, _strg_dir) = create_test_learner().await;
Add::paper(&create_test_paper()).execute(&mut learner.database).await?;
Add::paper(&create_second_test_paper()).execute(&mut learner.database).await?;
let removed = Remove::from_query(Query::text("two")).execute(&mut learner.database).await?;
assert_eq!(removed.len(), 1);
assert_eq!(removed[0].title, "Test Paper: Two");
Ok(())
}
#[tokio::test]
#[traced_test]
async fn test_remove_by_author() -> TestResult<()> {
let (mut learner, _cfg_dir, _db_dir, _strg_dir) = create_test_learner().await;
Add::paper(&create_test_paper()).execute(&mut learner.database).await?;
Add::paper(&create_second_test_paper()).execute(&mut learner.database).await?;
let removed =
Remove::from_query(Query::by_author("John Doe")).execute(&mut learner.database).await?;
assert_eq!(removed.len(), 1);
assert_eq!(removed[0].authors[0].name, "John Doe");
let remaining = Query::list_all().execute(&mut learner.database).await?;
assert_eq!(remaining.len(), 1);
Ok(())
}
#[tokio::test]
#[traced_test]
async fn test_remove_with_ordering() -> TestResult<()> {
let (mut learner, _cfg_dir, _db_dir, _strg_dir) = create_test_learner().await;
Add::paper(&create_test_paper()).execute(&mut learner.database).await?;
Add::paper(&create_second_test_paper()).execute(&mut learner.database).await?;
let removed =
Remove::from_query(Query::text("test").order_by(OrderField::PublicationDate).descending())
.execute(&mut learner.database)
.await?;
assert_eq!(removed.len(), 2);
assert_eq!(removed[0].title, "Test Paper: Two"); assert_eq!(removed[1].title, "Test Paper");
Ok(())
}
#[tokio::test]
#[traced_test]
async fn test_remove_by_date_range() -> TestResult<()> {
let (mut learner, _cfg_dir, _db_dir, _strg_dir) = create_test_learner().await;
Add::paper(&create_test_paper()).execute(&mut learner.database).await?;
Add::paper(&create_second_test_paper()).execute(&mut learner.database).await?;
let cutoff_date = Utc.with_ymd_and_hms(2024, 1, 1, 0, 0, 0).unwrap();
let removed =
Remove::from_query(Query::before_date(cutoff_date).order_by(OrderField::PublicationDate))
.execute(&mut learner.database)
.await?;
assert_eq!(removed.len(), 1);
assert_eq!(removed[0].title, "Test Paper");
Ok(())
}
#[tokio::test]
#[traced_test]
async fn test_remove_multiple_papers_by_source() -> TestResult<()> {
let (mut learner, _cfg_dir, _db_dir, _strg_dir) = create_test_learner().await;
let paper1 = create_test_paper();
let paper2 = create_second_test_paper();
Add::paper(&paper1).execute(&mut learner.database).await?;
Add::paper(&paper2).execute(&mut learner.database).await?;
let removed = Remove::from_query(Query::text("test")).execute(&mut learner.database).await?;
assert_eq!(removed.len(), 2);
assert!(removed.iter().all(|p| p.source == "arxiv"));
let remaining = Query::text("test").execute(&mut learner.database).await?;
assert!(remaining.is_empty());
Ok(())
}
#[tokio::test]
#[traced_test]
async fn test_remove_multiple_papers_from_source() -> TestResult<()> {
let (mut learner, _cfg_dir, _db_dir, _strg_dir) = create_test_learner().await;
let paper1 = create_test_paper();
let paper2 = create_second_test_paper();
Add::paper(&paper1).execute(&mut learner.database).await?;
Add::paper(&paper2).execute(&mut learner.database).await?;
let initial = Query::list_all().execute(&mut learner.database).await?;
assert!(initial.iter().any(|p| p.source == "arxiv"));
let removed = Remove::from_query(Query::list_all().order_by(OrderField::Source))
.execute(&mut learner.database)
.await?;
let arxiv_count = removed.iter().filter(|p| p.source == "arxiv").count();
assert_eq!(arxiv_count, 2);
let remaining = Query::list_all().execute(&mut learner.database).await?;
assert!(!remaining.iter().any(|p| p.source == "arxiv"));
Ok(())
}
}
mod recovery {
use super::*;
#[tokio::test]
#[traced_test]
async fn test_remove_papers_can_be_readded() -> TestResult<()> {
let (mut learner, _cfg_dir, _db_dir, _strg_dir) = create_test_learner().await;
let paper = create_test_paper();
Add::paper(&paper).execute(&mut learner.database).await?;
let removed_papers =
Remove::from_query(Query::text("test")).execute(&mut learner.database).await?;
assert_eq!(removed_papers.len(), 1);
Add::paper(&removed_papers[0]).execute(&mut learner.database).await?;
let results = Query::text("test").execute(&mut learner.database).await?;
assert_eq!(results.len(), 1);
Ok(())
}
#[tokio::test]
#[traced_test]
async fn test_bulk_remove_and_readd() -> TestResult<()> {
let (mut learner, _cfg_dir, _db_dir, _strg_dir) = create_test_learner().await;
Add::paper(&create_test_paper()).execute(&mut learner.database).await?;
Add::paper(&create_second_test_paper()).execute(&mut learner.database).await?;
let removed = Remove::from_query(Query::text("test")).execute(&mut learner.database).await?;
assert_eq!(removed.len(), 2);
for paper in &removed {
Add::paper(paper).execute(&mut learner.database).await?;
}
let results = Query::text("test").execute(&mut learner.database).await?;
assert_eq!(results.len(), 2);
assert_eq!(results[0].title, removed[0].title);
assert_eq!(results[1].title, removed[1].title);
Ok(())
}
#[tokio::test]
#[traced_test]
async fn test_readd_with_different_completion() -> TestResult<()> {
let (mut learner, _cfg_dir, _db_dir, _strg_dir) = create_test_learner().await;
let paper = learner.retriever.get_paper("https://arxiv.org/abs/2301.07041").await?;
Add::paper(&paper).execute(&mut learner.database).await?;
let removed = Remove::by_source(&paper.source, &paper.source_identifier)
.execute(&mut learner.database)
.await?;
Add::complete(&removed[0]).execute(&mut learner.database).await?;
let results = Query::by_source(&paper.source, &paper.source_identifier)
.execute(&mut learner.database)
.await?;
assert_eq!(results.len(), 1);
Ok(())
}
#[tokio::test]
#[traced_test]
async fn test_remove_and_readd_preserves_metadata() -> TestResult<()> {
let (mut learner, _cfg_dir, _db_dir, _strg_dir) = create_test_learner().await;
let mut paper = create_test_paper();
paper.doi = Some("10.1234/test".to_string());
paper.pdf_url = Some("https://example.com/test.pdf".to_string());
Add::paper(&paper).execute(&mut learner.database).await?;
let removed = Remove::by_source(&paper.source, &paper.source_identifier)
.execute(&mut learner.database)
.await?;
Add::paper(&removed[0]).execute(&mut learner.database).await?;
let results = Query::by_source(&paper.source, &paper.source_identifier)
.execute(&mut learner.database)
.await?;
assert_eq!(results[0].doi, paper.doi);
assert_eq!(results[0].pdf_url, paper.pdf_url);
Ok(())
}
#[tokio::test]
#[traced_test]
async fn test_remove_readd_with_updated_data() -> TestResult<()> {
let (mut learner, _cfg_dir, _db_dir, _strg_dir) = create_test_learner().await;
let paper = create_test_paper();
Add::paper(&paper).execute(&mut learner.database).await?;
let mut removed = Remove::by_source(&paper.source, &paper.source_identifier)
.execute(&mut learner.database)
.await?;
let mut updated_paper = removed.remove(0);
updated_paper.abstract_text = "Updated abstract".to_string();
updated_paper.doi = Some("10.1234/new".to_string());
Add::paper(&updated_paper).execute(&mut learner.database).await?;
let results = Query::by_source(&paper.source, &paper.source_identifier)
.execute(&mut learner.database)
.await?;
assert_eq!(results[0].abstract_text, "Updated abstract");
assert_eq!(results[0].doi, Some("10.1234/new".to_string()));
Ok(())
}
}