Skip to content

Commit c456b6c

Browse files
committed
chore: updated tests
1 parent 36b69f7 commit c456b6c

1 file changed

Lines changed: 180 additions & 0 deletions

File tree

src/app/tests.rs

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2159,6 +2159,71 @@ fn test_abs_previous_no_selection() {
21592159
assert_eq!(app.abs_state.selected(), Some(0));
21602160
}
21612161

2162+
// ─── AddModal: Selection → InputAbs via Char('b') ────────────────────────────
2163+
2164+
#[test]
2165+
fn test_add_modal_selection_char_b_opens_input_abs() {
2166+
let mut app = App::new_test();
2167+
app.add_modal_state = Some(AddModalState::Selection);
2168+
app.handle_add_modal_input(KeyCode::Char('b'));
2169+
assert!(matches!(app.add_modal_state, Some(AddModalState::InputAbs { .. })));
2170+
}
2171+
2172+
// ─── abs_play_next_episode: body coverage ────────────────────────────────────
2173+
2174+
#[test]
2175+
fn test_abs_play_next_all_episodes_filtered_out() {
2176+
// Covers the None => 0 branch and the find-returning-None path (no network).
2177+
let mut app = App::new_test();
2178+
app.abs_current_library_item_id = Some("pod1".to_string());
2179+
app.abs_current_episode_id = None; // covers None => 0 branch
2180+
app.abs_episodes = vec![make_abs_ep("ep1", 1000, true)]; // finished
2181+
app.abs_hide_played = true; // filter them out → find returns None → no network call
2182+
app.abs_clients = vec![crate::audiobookshelf::AudioBookshelfClient::new(
2183+
crate::config::AbsSourceConfig {
2184+
server_url: "http://127.0.0.1:1".to_string(),
2185+
username: "u".to_string(),
2186+
api_token: "t".to_string(),
2187+
},
2188+
)];
2189+
app.abs_play_next_episode(); // should not panic and make no network call
2190+
assert!(app.notification.is_none());
2191+
}
2192+
2193+
#[test]
2194+
fn test_abs_play_next_network_fail_covers_err_branch() {
2195+
// Covers the Some(id) → position path AND the Err branch from the network call.
2196+
// Port 1 on loopback fails with ECONNREFUSED instantly.
2197+
let mut app = App::new_test();
2198+
app.abs_current_library_item_id = Some("pod1".to_string());
2199+
// current episode ep0 is NOT in abs_episodes → unwrap_or(0) kicks in, but
2200+
// more importantly this exercises the Some(id) → position branch.
2201+
app.abs_current_episode_id = Some("ep0".to_string());
2202+
app.abs_episodes = vec![make_abs_ep("ep1", 1000, false)];
2203+
app.abs_hide_played = false;
2204+
app.abs_clients = vec![crate::audiobookshelf::AudioBookshelfClient::new(
2205+
crate::config::AbsSourceConfig {
2206+
server_url: "http://127.0.0.1:1".to_string(), // guaranteed ECONNREFUSED
2207+
username: "u".to_string(),
2208+
api_token: "t".to_string(),
2209+
},
2210+
)];
2211+
app.abs_play_next_episode();
2212+
// The Err branch sets a notification
2213+
assert!(app.notification.is_some());
2214+
}
2215+
2216+
// ─── enter_directory: Subsonic with no clients ───────────────────────────────
2217+
2218+
#[test]
2219+
fn test_enter_directory_subsonic_no_clients_returns_early() {
2220+
let mut app = App::new_test();
2221+
app.mode = AppMode::Subsonic;
2222+
app.subsonic_clients = vec![];
2223+
app.subsonic_state.select(Some(0));
2224+
app.enter_directory(); // hits the `return` at line 809
2225+
}
2226+
21622227
// ─── Search in Subsonic / Favorites modes ───────────────────────────────────
21632228

21642229
#[test]
@@ -2250,3 +2315,118 @@ fn test_enter_directory_favorites_station() {
22502315
app.enter_directory(); // plays the station
22512316
// Just verify it doesn't panic; play_radio spawns a thread
22522317
}
2318+
2319+
// ─── Additional edge-case coverage ───────────────────────────────────────────
2320+
2321+
#[test]
2322+
fn test_radio_previous_no_selection() {
2323+
// Covers None => 0 branch in previous() for Radio mode
2324+
let mut app = App::new_test();
2325+
app.mode = AppMode::Radio;
2326+
app.radio_groups = vec![crate::radio::RadioGroup {
2327+
title: "G".to_string(),
2328+
stations: vec![],
2329+
is_expanded: false,
2330+
}];
2331+
app.update_search_results();
2332+
app.radio_state.select(None);
2333+
app.previous();
2334+
assert_eq!(app.radio_state.selected(), Some(0));
2335+
}
2336+
2337+
#[test]
2338+
fn test_enter_directory_filesystem_no_selection() {
2339+
// Covers the None branch of the if-let in FileSystem enter_directory
2340+
let mut app = App::new_test();
2341+
app.mode = AppMode::FileSystem;
2342+
app.state.select(None);
2343+
app.enter_directory(); // no-op, should not panic
2344+
}
2345+
2346+
#[test]
2347+
fn test_enter_directory_favorites_no_selection() {
2348+
// Covers the None branch of favorites_state.selected()
2349+
let mut app = App::new_test();
2350+
app.mode = AppMode::Favorites;
2351+
app.favorites_state.select(None);
2352+
app.enter_directory(); // no-op
2353+
}
2354+
2355+
#[test]
2356+
fn test_enter_directory_radio_no_groups_action_none() {
2357+
// Covers the None => {} arm when no groups match (action stays None)
2358+
let mut app = App::new_test();
2359+
app.mode = AppMode::Radio;
2360+
// No groups → filtered_radio_groups empty → loop never fires → action = None
2361+
app.radio_state.select(Some(0));
2362+
app.enter_directory(); // reaches None => {}
2363+
}
2364+
2365+
#[test]
2366+
fn test_enter_directory_radio_loop_skips_expanded_group() {
2367+
// Covers current_idx += group.stations.len() when selection is past an expanded group
2368+
let station = crate::radio::RadioStation {
2369+
name: "S1".to_string(),
2370+
url: "http://s1".to_string(),
2371+
description: None,
2372+
homepage: None,
2373+
tags: None,
2374+
last_playing: None,
2375+
};
2376+
let mut app = App::new_test();
2377+
app.mode = AppMode::Radio;
2378+
app.radio_groups = vec![
2379+
crate::radio::RadioGroup {
2380+
title: "Group 1".to_string(),
2381+
stations: vec![station],
2382+
is_expanded: true,
2383+
},
2384+
crate::radio::RadioGroup {
2385+
title: "Group 2".to_string(),
2386+
stations: vec![],
2387+
is_expanded: false,
2388+
},
2389+
];
2390+
app.update_search_results();
2391+
// Index layout: 0=G1header, 1=S1, 2=G2header
2392+
// Selecting G2header (index 2) forces the loop to skip past G1's stations
2393+
app.radio_state.select(Some(2));
2394+
app.enter_directory(); // ToggleGroup(1) after skipping G1's expanded stations
2395+
assert!(app.radio_groups[1].is_expanded);
2396+
}
2397+
2398+
#[test]
2399+
fn test_get_selected_station_past_first_group() {
2400+
// Covers the current_idx += group.stations.len() path and the final None return
2401+
let make_station = |name: &str, url: &str| crate::radio::RadioStation {
2402+
name: name.to_string(),
2403+
url: url.to_string(),
2404+
description: None,
2405+
homepage: None,
2406+
tags: None,
2407+
last_playing: None,
2408+
};
2409+
let mut app = App::new_test();
2410+
app.radio_groups = vec![
2411+
crate::radio::RadioGroup {
2412+
title: "G1".to_string(),
2413+
stations: vec![make_station("S1", "http://s1"), make_station("S2", "http://s2")],
2414+
is_expanded: true,
2415+
},
2416+
crate::radio::RadioGroup {
2417+
title: "G2".to_string(),
2418+
stations: vec![make_station("S3", "http://s3")],
2419+
is_expanded: true,
2420+
},
2421+
];
2422+
app.update_search_results();
2423+
// Index layout: 0=G1h, 1=S1, 2=S2, 3=G2h, 4=S3
2424+
// G2 header → should return None
2425+
app.radio_state.select(Some(3));
2426+
assert!(app.get_selected_station().is_none());
2427+
// S3 → should return Some, requiring loop to skip G1's stations
2428+
app.radio_state.select(Some(4));
2429+
let s = app.get_selected_station();
2430+
assert!(s.is_some());
2431+
assert_eq!(s.unwrap().name, "S3");
2432+
}

0 commit comments

Comments
 (0)