Reviewed-on: #26
This commit was merged in pull request #26.
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
use k_ap::ActorBlocklist;
|
||||
use sqlite_federation::SqliteFederationRepository;
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
async fn test_pool() -> SqlitePool {
|
||||
let pool = SqlitePool::connect("sqlite::memory:").await.unwrap();
|
||||
sqlx::query(
|
||||
"CREATE TABLE users (id TEXT PRIMARY KEY, email TEXT, password_hash TEXT, created_at TEXT)",
|
||||
)
|
||||
.execute(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
sqlx::query("CREATE TABLE blocked_actors (local_user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE, remote_actor_url TEXT NOT NULL, blocked_at TEXT NOT NULL, PRIMARY KEY (local_user_id, remote_actor_url))")
|
||||
.execute(&pool).await.unwrap();
|
||||
let uid = uuid::Uuid::new_v4().to_string();
|
||||
sqlx::query("INSERT INTO users (id, email, password_hash, created_at) VALUES (?, ?, ?, ?)")
|
||||
.bind(&uid)
|
||||
.bind("a@b.com")
|
||||
.bind("hash")
|
||||
.bind("2024-01-01")
|
||||
.execute(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
pool
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn blocked_actor_is_detected_listed_and_unblocked() {
|
||||
let pool = test_pool().await;
|
||||
let user_id = uuid::Uuid::parse_str(
|
||||
&sqlx::query_scalar::<_, String>("SELECT id FROM users LIMIT 1")
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
let repo = SqliteFederationRepository::new(pool);
|
||||
let actor_url = "https://mastodon.social/users/alice";
|
||||
assert!(!repo.is_actor_blocked(user_id, actor_url).await.unwrap());
|
||||
repo.add_blocked_actor(user_id, actor_url).await.unwrap();
|
||||
assert!(repo.is_actor_blocked(user_id, actor_url).await.unwrap());
|
||||
let list = repo.get_blocked_actors(user_id).await.unwrap();
|
||||
assert_eq!(list, vec![actor_url.to_string()]);
|
||||
repo.remove_blocked_actor(user_id, actor_url).await.unwrap();
|
||||
assert!(!repo.is_actor_blocked(user_id, actor_url).await.unwrap());
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
use chrono::Utc;
|
||||
use k_ap::AnnounceRepository;
|
||||
use sqlite_federation::SqliteFederationRepository;
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
async fn test_pool() -> SqlitePool {
|
||||
let pool = SqlitePool::connect("sqlite::memory:").await.unwrap();
|
||||
sqlx::query("CREATE TABLE ap_announces (id TEXT PRIMARY KEY, object_url TEXT NOT NULL, actor_url TEXT NOT NULL, announced_at TEXT NOT NULL)")
|
||||
.execute(&pool).await.unwrap();
|
||||
pool
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn added_announce_is_counted() {
|
||||
let pool = test_pool().await;
|
||||
let repo = SqliteFederationRepository::new(pool);
|
||||
repo.add_announce(
|
||||
"https://remote/ann/1",
|
||||
"https://local/r/1",
|
||||
"https://remote/u/1",
|
||||
Utc::now(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(repo.count_announces("https://local/r/1").await.unwrap(), 1);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn duplicate_announce_is_ignored() {
|
||||
let pool = test_pool().await;
|
||||
let repo = SqliteFederationRepository::new(pool);
|
||||
repo.add_announce(
|
||||
"https://remote/ann/1",
|
||||
"https://local/r/1",
|
||||
"https://remote/u/1",
|
||||
Utc::now(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
repo.add_announce(
|
||||
"https://remote/ann/1",
|
||||
"https://local/r/1",
|
||||
"https://remote/u/1",
|
||||
Utc::now(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(repo.count_announces("https://local/r/1").await.unwrap(), 1);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
use k_ap::DomainBlocklist;
|
||||
use sqlite_federation::SqliteFederationRepository;
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
async fn test_pool() -> SqlitePool {
|
||||
let pool = SqlitePool::connect("sqlite::memory:").await.unwrap();
|
||||
sqlx::query("CREATE TABLE blocked_domains (domain TEXT PRIMARY KEY, reason TEXT, blocked_at TEXT NOT NULL)")
|
||||
.execute(&pool).await.unwrap();
|
||||
pool
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn blocked_domain_is_detected() {
|
||||
let pool = test_pool().await;
|
||||
let repo = SqliteFederationRepository::new(pool);
|
||||
assert!(!repo.is_domain_blocked("mastodon.social").await.unwrap());
|
||||
repo.add_blocked_domain("mastodon.social", Some("spam"))
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(repo.is_domain_blocked("mastodon.social").await.unwrap());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn remove_unblocks_domain() {
|
||||
let pool = test_pool().await;
|
||||
let repo = SqliteFederationRepository::new(pool);
|
||||
repo.add_blocked_domain("spam.xyz", None).await.unwrap();
|
||||
repo.remove_blocked_domain("spam.xyz").await.unwrap();
|
||||
assert!(!repo.is_domain_blocked("spam.xyz").await.unwrap());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_blocked_domains_returns_all() {
|
||||
let pool = test_pool().await;
|
||||
let repo = SqliteFederationRepository::new(pool);
|
||||
repo.add_blocked_domain("a.com", Some("reason a"))
|
||||
.await
|
||||
.unwrap();
|
||||
repo.add_blocked_domain("b.com", None).await.unwrap();
|
||||
let domains = repo.get_blocked_domains().await.unwrap();
|
||||
assert_eq!(domains.len(), 2);
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
use activitypub::{NewRemoteReview, RemoteReviewRepository};
|
||||
use domain::{
|
||||
events::DomainEvent,
|
||||
models::{PersistedReview, Review, ReviewSource},
|
||||
value_objects::{Comment, ExternalMetadataId, MovieId, Rating, ReviewId, UserId},
|
||||
};
|
||||
use sqlite_federation::SqliteFederationRepository;
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
const ACTOR: &str = "https://remote.example/users/ann";
|
||||
const AP_ID: &str = "https://remote.example/reviews/1";
|
||||
|
||||
async fn migrated_pool() -> SqlitePool {
|
||||
let pool = sqlx::sqlite::SqlitePoolOptions::new()
|
||||
.max_connections(1)
|
||||
.connect("sqlite::memory:")
|
||||
.await
|
||||
.unwrap();
|
||||
sqlite::migrate(&pool).await.unwrap();
|
||||
pool
|
||||
}
|
||||
|
||||
async fn count(pool: &SqlitePool, table: &str) -> i64 {
|
||||
sqlx::query_scalar(&format!("SELECT COUNT(*) FROM {table}"))
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn remote_review(movie_id: &MovieId) -> Review {
|
||||
let now = chrono::Utc::now().naive_utc();
|
||||
Review::from_persistence(PersistedReview {
|
||||
id: ReviewId::generate(),
|
||||
movie_id: movie_id.clone(),
|
||||
user_id: UserId::generate(),
|
||||
rating: Rating::new(4).unwrap(),
|
||||
comment: Some(Comment::new("good".into()).unwrap()),
|
||||
watched_at: now,
|
||||
created_at: now,
|
||||
source: ReviewSource::Remote {
|
||||
actor_url: ACTOR.into(),
|
||||
},
|
||||
watch_medium: None,
|
||||
spoilers: false,
|
||||
})
|
||||
}
|
||||
|
||||
fn enrichment_requested(movie_id: &MovieId) -> DomainEvent {
|
||||
DomainEvent::MovieEnrichmentRequested {
|
||||
movie_id: movie_id.clone(),
|
||||
external_metadata_id: ExternalMetadataId::new("tmdb:603".into()).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn save(pool: &SqlitePool, review: &Review, events: &[DomainEvent]) -> anyhow::Result<()> {
|
||||
SqliteFederationRepository::new(pool.clone())
|
||||
.save_remote_review(NewRemoteReview {
|
||||
review,
|
||||
ap_id: AP_ID,
|
||||
movie_title: "The Matrix",
|
||||
release_year: 1999,
|
||||
external_metadata_id: Some("tmdb:603"),
|
||||
poster_url: None,
|
||||
events,
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn saving_a_remote_review_writes_it_and_its_event_in_one_commit() {
|
||||
let pool = migrated_pool().await;
|
||||
let movie_id = MovieId::generate();
|
||||
let review = remote_review(&movie_id);
|
||||
|
||||
save(&pool, &review, &[enrichment_requested(&movie_id)])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(count(&pool, "reviews").await, 1);
|
||||
assert_eq!(count(&pool, "movies").await, 1);
|
||||
assert_eq!(count(&pool, "event_queue").await, 1);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn a_failing_enqueue_rolls_back_the_remote_review_and_its_movie() {
|
||||
let pool = migrated_pool().await;
|
||||
sqlx::query(
|
||||
"CREATE TRIGGER fail_event_insert BEFORE INSERT ON event_queue \
|
||||
BEGIN SELECT RAISE(ABORT, 'enqueue failed'); END",
|
||||
)
|
||||
.execute(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
let movie_id = MovieId::generate();
|
||||
let review = remote_review(&movie_id);
|
||||
|
||||
let result = save(&pool, &review, &[enrichment_requested(&movie_id)]).await;
|
||||
|
||||
assert!(result.is_err());
|
||||
assert_eq!(count(&pool, "reviews").await, 0);
|
||||
assert_eq!(count(&pool, "movies").await, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn a_remote_review_without_events_enqueues_nothing() {
|
||||
let pool = migrated_pool().await;
|
||||
let movie_id = MovieId::generate();
|
||||
|
||||
save(&pool, &remote_review(&movie_id), &[]).await.unwrap();
|
||||
|
||||
assert_eq!(count(&pool, "reviews").await, 1);
|
||||
assert_eq!(count(&pool, "event_queue").await, 0);
|
||||
}
|
||||
Reference in New Issue
Block a user