mirror of https://github.com/synctv-org/synctv
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
327 lines
10 KiB
Rust
327 lines
10 KiB
Rust
//! Media cache invalidation tests
|
|
//!
|
|
//! Tests that media edits properly broadcast events.
|
|
//!
|
|
|
|
use chrono::Utc;
|
|
use std::sync::Arc;
|
|
use std::time::Duration;
|
|
use synctv_core::{
|
|
models::{
|
|
Playlist, PlaylistId, Room, RoomId, RoomMember, RoomRole, RoomStatus, SourceProvider, User,
|
|
UserId, UserRole, UserStatus,
|
|
},
|
|
repository::{
|
|
MediaRepository, PlaylistRepository, RoomMemberRepository, RoomRepository, UserRepository,
|
|
},
|
|
service::{
|
|
AddMediaRequest, EditMediaRequest, MediaService, NotificationService, PermissionService,
|
|
ProvidersManager, RoomEvent,
|
|
},
|
|
};
|
|
use synctv_core_testing::{create_test_pool, ok};
|
|
|
|
fn make_user(username: &str) -> User {
|
|
let now = Utc::now();
|
|
User {
|
|
id: UserId::new(),
|
|
username: username.to_string(),
|
|
role: UserRole::User,
|
|
avatar_file_reference_id: None,
|
|
status: UserStatus::Active,
|
|
signup_method: synctv_core::models::SignupMethod::Email,
|
|
created_at: now,
|
|
updated_at: now,
|
|
version: 0,
|
|
deleted_at: None,
|
|
is_banned: false,
|
|
banned_at: None,
|
|
banned_by: None,
|
|
banned_reason: None,
|
|
}
|
|
}
|
|
|
|
struct MediaEditFixture {
|
|
room: Room,
|
|
owner: User,
|
|
playlist: Playlist,
|
|
media_service: MediaService,
|
|
notification_service: NotificationService,
|
|
}
|
|
|
|
async fn setup_media_edit_fixture(
|
|
pool: &sqlx::PgPool,
|
|
owner_name: &str,
|
|
room_name: &str,
|
|
notification_service: NotificationService,
|
|
) -> MediaEditFixture {
|
|
let user_repo = UserRepository::new(pool.clone());
|
|
let room_repo = RoomRepository::new(pool.clone());
|
|
let playlist_repo = PlaylistRepository::new(pool.clone());
|
|
let media_repo = MediaRepository::new(pool.clone());
|
|
|
|
let owner = ok(
|
|
user_repo.create(&make_user(owner_name)).await,
|
|
"media edit owner should be created",
|
|
);
|
|
let room = ok(
|
|
room_repo
|
|
.create(&{
|
|
let now = Utc::now();
|
|
Room {
|
|
id: RoomId::new(),
|
|
name: room_name.to_string(),
|
|
description: String::new(),
|
|
cover_file_reference_id: None,
|
|
category: None,
|
|
labels: Vec::new(),
|
|
created_by: owner.id,
|
|
status: RoomStatus::Active,
|
|
is_banned: false,
|
|
is_public: true,
|
|
closed_at: None,
|
|
created_at: now,
|
|
updated_at: now,
|
|
deleted_at: None,
|
|
version: 0,
|
|
last_activity_at: now,
|
|
}
|
|
})
|
|
.await,
|
|
"media edit room should be created",
|
|
);
|
|
|
|
let member_repo_setup = RoomMemberRepository::new(pool.clone());
|
|
let owner_member = RoomMember {
|
|
room_id: room.id,
|
|
user_id: owner.id,
|
|
role: RoomRole::Creator,
|
|
added_permissions: 0,
|
|
removed_permissions: 0,
|
|
admin_added_permissions: 0,
|
|
admin_removed_permissions: 0,
|
|
remark_name: String::new(),
|
|
display_tag: String::new(),
|
|
joined_at: Utc::now(),
|
|
version: 0,
|
|
};
|
|
ok(
|
|
member_repo_setup.add(&owner_member).await,
|
|
"media edit owner membership should be created",
|
|
);
|
|
|
|
let playlist = ok(
|
|
playlist_repo
|
|
.create(&{
|
|
let now = Utc::now();
|
|
Playlist {
|
|
id: PlaylistId::new(),
|
|
room_id: room.id,
|
|
creator_id: Some(owner.id),
|
|
browse_access_mode: synctv_core::models::PlaylistBrowseAccessMode::Default,
|
|
name: String::new(),
|
|
description: String::new(),
|
|
cover_file_reference_id: None,
|
|
parent_id: None,
|
|
position: 0.0,
|
|
source_provider: None,
|
|
source_config: None,
|
|
provider_instance_name: None,
|
|
created_at: now,
|
|
updated_at: now,
|
|
version: 0,
|
|
}
|
|
})
|
|
.await,
|
|
"media edit playlist should be created",
|
|
);
|
|
|
|
let member_repo = synctv_core::repository::RoomMemberRepository::new(pool.clone());
|
|
let permission_service = ok(
|
|
PermissionService::new(member_repo, room_repo.clone(), None, 1000, 300),
|
|
"permission service should build",
|
|
);
|
|
|
|
let provider_repo = synctv_core::repository::ProviderInstanceRepository::new(pool.clone());
|
|
let provider = synctv_core::models::ProviderInstance {
|
|
name: "direct_url".to_string(),
|
|
endpoint: "http://localhost:50051".to_string(),
|
|
comment: None,
|
|
jwt_secret: None,
|
|
custom_ca: None,
|
|
timeout: "30".to_string(),
|
|
tls: false,
|
|
insecure_tls: false,
|
|
providers: vec![SourceProvider::DirectUrl],
|
|
enabled: true,
|
|
created_at: Utc::now(),
|
|
updated_at: Utc::now(),
|
|
};
|
|
ok(
|
|
provider_repo.create(&provider).await,
|
|
"direct_url provider row should be created",
|
|
);
|
|
|
|
let provider_instance_repo =
|
|
synctv_core::repository::ProviderInstanceRepository::new(pool.clone());
|
|
let remote_provider_manager =
|
|
synctv_core::service::RemoteProviderManager::new(Arc::new(provider_instance_repo));
|
|
let providers_manager = Arc::new(ok(
|
|
ProvidersManager::new(Arc::new(remote_provider_manager)),
|
|
"providers manager should build",
|
|
));
|
|
|
|
ok(
|
|
providers_manager
|
|
.create_provider_with_default_config("direct_url", "direct_url")
|
|
.await,
|
|
"direct_url provider instance should be registered",
|
|
);
|
|
|
|
let media_service = MediaService::new(
|
|
media_repo,
|
|
playlist_repo,
|
|
permission_service,
|
|
providers_manager,
|
|
notification_service.clone(),
|
|
);
|
|
|
|
MediaEditFixture {
|
|
room,
|
|
owner,
|
|
playlist,
|
|
media_service,
|
|
notification_service,
|
|
}
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
|
#[ignore = "Requires Docker"]
|
|
async fn test_edit_media_sends_notification() {
|
|
let (_container, pool) = create_test_pool().await;
|
|
let fixture = setup_media_edit_fixture(
|
|
&pool,
|
|
"test_owner",
|
|
"Test Room",
|
|
NotificationService::default(),
|
|
)
|
|
.await;
|
|
let mut rx = fixture.notification_service.subscribe();
|
|
|
|
let add_req = AddMediaRequest {
|
|
playlist_id: Some(fixture.playlist.id),
|
|
name: "Test Media".to_string(),
|
|
description: String::new(),
|
|
source_provider: SourceProvider::DirectUrl,
|
|
provider_instance_name: None,
|
|
source_config: synctv_core_testing::direct_url_media_source_config(
|
|
"https://example.com/video.mp4",
|
|
),
|
|
};
|
|
|
|
let media = ok(
|
|
fixture
|
|
.media_service
|
|
.add_media(fixture.room.id, fixture.owner.id, add_req)
|
|
.await,
|
|
"media should be added before edit",
|
|
);
|
|
|
|
let edit_req = EditMediaRequest {
|
|
media_id: media.id,
|
|
name: Some("Updated Media".to_string()),
|
|
description: None,
|
|
playback_proxy_mode: None,
|
|
source_config: None,
|
|
provider_instance_name: None,
|
|
};
|
|
|
|
let updated_media = ok(
|
|
fixture
|
|
.media_service
|
|
.edit_media(fixture.room.id, fixture.owner.id, edit_req)
|
|
.await,
|
|
"media should be edited",
|
|
);
|
|
|
|
assert_eq!(updated_media.name, "Updated Media");
|
|
|
|
let mut found_update = false;
|
|
for _ in 0..10 {
|
|
let result = tokio::time::timeout(Duration::from_secs(2), rx.recv()).await;
|
|
match result {
|
|
Ok(Ok((notif_room_id, event))) => {
|
|
assert_eq!(notif_room_id, fixture.room.id);
|
|
match event {
|
|
RoomEvent::MediaUpdated {
|
|
media_id, title, ..
|
|
} => {
|
|
assert_eq!(media_id, media.id);
|
|
assert_eq!(title, "Updated Media");
|
|
found_update = true;
|
|
break;
|
|
}
|
|
RoomEvent::MediaAdded { .. } => {}
|
|
other => std::panic::panic_any(format!(
|
|
"unexpected notification event while waiting for MediaUpdated: {other:?}"
|
|
)),
|
|
}
|
|
}
|
|
Ok(Err(e)) => std::panic::panic_any(format!("channel error: {e}")),
|
|
Err(_) => break,
|
|
}
|
|
}
|
|
assert!(found_update, "Expected to receive MediaUpdated event");
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
|
#[ignore = "Requires Docker"]
|
|
async fn test_edit_media_without_notification_service_succeeds() {
|
|
let (_container, pool) = create_test_pool().await;
|
|
let fixture = setup_media_edit_fixture(
|
|
&pool,
|
|
"test_owner2",
|
|
"Test Room 2",
|
|
NotificationService::default(),
|
|
)
|
|
.await;
|
|
|
|
let add_req = AddMediaRequest {
|
|
playlist_id: Some(fixture.playlist.id),
|
|
name: "Test Media".to_string(),
|
|
description: String::new(),
|
|
source_provider: SourceProvider::DirectUrl,
|
|
provider_instance_name: None,
|
|
source_config: synctv_core_testing::direct_url_media_source_config(
|
|
"https://example.com/video.mp4",
|
|
),
|
|
};
|
|
|
|
let media = ok(
|
|
fixture
|
|
.media_service
|
|
.add_media(fixture.room.id, fixture.owner.id, add_req)
|
|
.await,
|
|
"media should be added before edit",
|
|
);
|
|
|
|
let edit_req = EditMediaRequest {
|
|
media_id: media.id,
|
|
name: Some("Updated Media".to_string()),
|
|
description: None,
|
|
playback_proxy_mode: None,
|
|
source_config: None,
|
|
provider_instance_name: None,
|
|
};
|
|
|
|
let updated_media = ok(
|
|
fixture
|
|
.media_service
|
|
.edit_media(fixture.room.id, fixture.owner.id, edit_req)
|
|
.await,
|
|
"media edit should succeed with default notification service",
|
|
);
|
|
|
|
assert_eq!(updated_media.name, "Updated Media");
|
|
}
|