Files
k-tv/crates/application/src/channels/tests/update.rs

259 lines
6.4 KiB
Rust

use std::sync::Arc;
use domain::testing::{InMemoryChannelRepository, NoopEventPublisher};
use domain::value_objects::{ChannelId, UserId};
use domain::DomainError;
use crate::channels::commands::{CreateChannelCommand, UpdateChannelCommand};
use crate::channels::deps::ChannelCommandDeps;
use crate::channels::{create, update};
fn make_deps() -> (ChannelCommandDeps, Arc<InMemoryChannelRepository>) {
let repo = Arc::new(InMemoryChannelRepository::new());
let deps = ChannelCommandDeps {
channel_command: repo.clone(),
channel_query: repo.clone(),
event_publisher: Arc::new(NoopEventPublisher::new()),
};
(deps, repo)
}
#[tokio::test]
async fn updates_channel_name() {
let (deps, _) = make_deps();
let owner = UserId::generate();
let channel = create::execute(
&deps,
CreateChannelCommand {
owner_id: owner,
name: "Original".into(),
timezone: "UTC".into(),
},
)
.await
.unwrap();
let updated = update::execute(
&deps,
UpdateChannelCommand {
channel_id: channel.id(),
owner_id: owner,
name: Some("Renamed".into()),
description: None,
timezone: None,
schedule_config: None,
rotation_policy: None,
auto_schedule: None,
gap_filler: None,
},
)
.await
.unwrap();
assert_eq!(updated.name(), "Renamed");
assert_eq!(updated.timezone(), "UTC"); // unchanged
}
#[tokio::test]
async fn update_fails_if_not_owner() {
let (deps, _) = make_deps();
let owner = UserId::generate();
let stranger = UserId::generate();
let channel = create::execute(
&deps,
CreateChannelCommand {
owner_id: owner,
name: "Protected".into(),
timezone: "UTC".into(),
},
)
.await
.unwrap();
let result = update::execute(
&deps,
UpdateChannelCommand {
channel_id: channel.id(),
owner_id: stranger,
name: Some("Hacked".into()),
description: None,
timezone: None,
schedule_config: None,
rotation_policy: None,
auto_schedule: None,
gap_filler: None,
},
)
.await;
assert!(result.is_err());
match result.unwrap_err() {
DomainError::Forbidden(_) => {}
other => panic!("expected Forbidden, got: {:?}", other),
}
}
#[tokio::test]
async fn update_nonexistent_channel_returns_not_found() {
let (deps, _) = make_deps();
let result = update::execute(
&deps,
UpdateChannelCommand {
channel_id: ChannelId::generate(),
owner_id: UserId::generate(),
name: Some("Ghost".into()),
description: None,
timezone: None,
schedule_config: None,
rotation_policy: None,
auto_schedule: None,
gap_filler: None,
},
)
.await;
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
DomainError::ChannelNotFound(_)
));
}
#[tokio::test]
async fn update_config_creates_snapshot() {
let (deps, repo) = make_deps();
let owner = UserId::generate();
let channel = create::execute(
&deps,
CreateChannelCommand {
owner_id: owner,
name: "Snapshotted".into(),
timezone: "UTC".into(),
},
)
.await
.unwrap();
// Update with new schedule_config
let new_config = domain::models::ScheduleConfig::default();
update::execute(
&deps,
UpdateChannelCommand {
channel_id: channel.id(),
owner_id: owner,
name: None,
description: None,
timezone: None,
schedule_config: Some(new_config),
rotation_policy: None,
auto_schedule: None,
gap_filler: None,
},
)
.await
.unwrap();
// Verify a config snapshot was created
let snapshots = repo.snapshots.lock().unwrap();
assert_eq!(snapshots.len(), 1);
assert_eq!(snapshots[0].channel_id(), channel.id());
}
#[tokio::test]
async fn update_without_config_skips_snapshot() {
let (deps, repo) = make_deps();
let owner = UserId::generate();
let channel = create::execute(
&deps,
CreateChannelCommand {
owner_id: owner,
name: "NoSnapshot".into(),
timezone: "UTC".into(),
},
)
.await
.unwrap();
// Update name only — no config change
update::execute(
&deps,
UpdateChannelCommand {
channel_id: channel.id(),
owner_id: owner,
name: Some("Renamed".into()),
description: None,
timezone: None,
schedule_config: None,
rotation_policy: None,
auto_schedule: None,
gap_filler: None,
},
)
.await
.unwrap();
// No snapshot should exist
let snapshots = repo.snapshots.lock().unwrap();
assert!(snapshots.is_empty());
}
#[tokio::test]
async fn update_description_clear() {
let (deps, _) = make_deps();
let owner = UserId::generate();
let channel = create::execute(
&deps,
CreateChannelCommand {
owner_id: owner,
name: "Desc Test".into(),
timezone: "UTC".into(),
},
)
.await
.unwrap();
// Set description
let updated = update::execute(
&deps,
UpdateChannelCommand {
channel_id: channel.id(),
owner_id: owner,
name: None,
description: Some(Some("A description".into())),
timezone: None,
schedule_config: None,
rotation_policy: None,
auto_schedule: None,
gap_filler: None,
},
)
.await
.unwrap();
assert_eq!(updated.description(), Some("A description"));
// Clear description with Some(None)
let cleared = update::execute(
&deps,
UpdateChannelCommand {
channel_id: channel.id(),
owner_id: owner,
name: None,
description: Some(None),
timezone: None,
schedule_config: None,
rotation_policy: None,
auto_schedule: None,
gap_filler: None,
},
)
.await
.unwrap();
assert!(cleared.description().is_none());
}