1use std::{collections::HashMap, fs, path::Path, time::Instant};
2
3use crate::index as pkgindex;
4use crate::state::{AppState, PackageDetails, PackageItem};
5
6use super::super::deps_cache;
7use super::super::files_cache;
8use super::super::sandbox_cache;
9use super::super::services_cache;
10
11pub fn initialize_locale_system(
28 app: &mut AppState,
29 locale_pref: &str,
30 _prefs: &crate::theme::Settings,
31) {
32 let locales_dir = crate::i18n::find_locales_dir().unwrap_or_else(|| {
34 std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
35 .join("config")
36 .join("locales")
37 });
38 let Some(i18n_config_path) = crate::i18n::find_config_file("i18n.yml") else {
39 tracing::error!(
40 "i18n config file not found in development or installed locations. Using default locale 'en-US'."
41 );
42 app.locale = "en-US".to_string();
43 app.translations = std::collections::HashMap::new();
44 app.translations_fallback = std::collections::HashMap::new();
45 return;
46 };
47
48 let resolver = crate::i18n::LocaleResolver::new(&i18n_config_path);
50 let resolved_locale = resolver.resolve(locale_pref);
51
52 tracing::info!(
53 "Resolved locale: '{}' (from settings: '{}')",
54 &resolved_locale,
55 if locale_pref.trim().is_empty() {
56 "<auto-detect>"
57 } else {
58 locale_pref
59 }
60 );
61 app.locale.clone_from(&resolved_locale);
62
63 let mut loader = crate::i18n::LocaleLoader::new(locales_dir);
65
66 match loader.load("en-US") {
68 Ok(fallback) => {
69 let key_count = fallback.len();
70 app.translations_fallback = fallback;
71 tracing::debug!("Loaded English fallback translations ({} keys)", key_count);
72 }
73 Err(e) => {
74 tracing::error!(
75 "Failed to load English fallback translations: {}. Application may show untranslated keys.",
76 e
77 );
78 app.translations_fallback = std::collections::HashMap::new();
79 }
80 }
81
82 if resolved_locale == "en-US" {
84 app.translations = app.translations_fallback.clone();
86 tracing::debug!("Using English as primary locale");
87 } else {
88 match loader.load(&resolved_locale) {
89 Ok(translations) => {
90 let key_count = translations.len();
91 app.translations = translations;
92 tracing::info!(
93 "Loaded translations for locale '{}' ({} keys)",
94 resolved_locale,
95 key_count
96 );
97 let test_keys = [
99 "app.details.footer.search_hint",
100 "app.details.footer.confirm_installation",
101 ];
102 for key in &test_keys {
103 if app.translations.contains_key(*key) {
104 tracing::debug!(" ✓ Key '{}' found in translations", key);
105 } else {
106 tracing::debug!(" ✗ Key '{}' NOT found in translations", key);
107 }
108 }
109 }
110 Err(e) => {
111 tracing::warn!(
112 "Failed to load translations for locale '{}': {}. Using English fallback.",
113 resolved_locale,
114 e
115 );
116 app.translations = std::collections::HashMap::new();
118 }
119 }
120 }
121}
122
123pub fn run_startup_config_preflight() -> crate::theme::Settings {
137 crate::theme::maybe_migrate_legacy_confs();
138 crate::theme::ensure_theme_keys_present();
139 let prefs = crate::theme::settings();
140 crate::theme::ensure_settings_keys_present(&prefs);
141 prefs
142}
143
144#[allow(clippy::struct_excessive_bools)]
160pub struct InitFlags {
161 pub needs_deps_resolution: bool,
163 pub needs_files_resolution: bool,
165 pub needs_services_resolution: bool,
167 pub needs_sandbox_resolution: bool,
169}
170
171fn load_cache_with_signature<T>(
187 install_list: &[crate::state::PackageItem],
188 cache_path: &std::path::PathBuf,
189 compute_signature: impl Fn(&[crate::state::PackageItem]) -> Vec<String>,
190 load_cache: impl Fn(&std::path::PathBuf, &[String]) -> Option<T>,
191 cache_name: &str,
192) -> (Option<T>, bool) {
193 if install_list.is_empty() {
194 return (None, false);
195 }
196
197 let signature = compute_signature(install_list);
198 load_cache(cache_path, &signature).map_or_else(
199 || {
200 tracing::info!(
201 "{} cache missing or invalid, will trigger background resolution",
202 cache_name
203 );
204 (None, true)
205 },
206 |cached| (Some(cached), false),
207 )
208}
209
210fn ensure_cache_parent_dir(path: &Path) {
221 if let Some(parent) = path.parent()
222 && let Err(error) = fs::create_dir_all(parent)
223 {
224 tracing::warn!(
225 path = %parent.display(),
226 %error,
227 "[Init] Failed to create cache directory"
228 );
229 }
230}
231
232fn initialize_cache_files(app: &AppState) {
244 let empty_signature: Vec<String> = Vec::new();
245
246 if !app.deps_cache_path.exists() {
247 ensure_cache_parent_dir(&app.deps_cache_path);
248 deps_cache::save_cache(&app.deps_cache_path, &empty_signature, &[]);
249 tracing::debug!(
250 path = %app.deps_cache_path.display(),
251 "[Init] Created empty dependency cache"
252 );
253 }
254
255 if !app.files_cache_path.exists() {
256 ensure_cache_parent_dir(&app.files_cache_path);
257 files_cache::save_cache(&app.files_cache_path, &empty_signature, &[]);
258 tracing::debug!(
259 path = %app.files_cache_path.display(),
260 "[Init] Created empty file cache"
261 );
262 }
263
264 if !app.services_cache_path.exists() {
265 ensure_cache_parent_dir(&app.services_cache_path);
266 services_cache::save_cache(&app.services_cache_path, &empty_signature, &[]);
267 tracing::debug!(
268 path = %app.services_cache_path.display(),
269 "[Init] Created empty service cache"
270 );
271 }
272
273 if !app.sandbox_cache_path.exists() {
274 ensure_cache_parent_dir(&app.sandbox_cache_path);
275 sandbox_cache::save_cache(&app.sandbox_cache_path, &empty_signature, &[]);
276 tracing::debug!(
277 path = %app.sandbox_cache_path.display(),
278 "[Init] Created empty sandbox cache"
279 );
280 }
281}
282
283pub fn apply_settings_to_app_state(app: &mut AppState, prefs: &crate::theme::Settings) {
294 app.layout_left_pct = prefs.layout_left_pct;
295 app.layout_center_pct = prefs.layout_center_pct;
296 app.layout_right_pct = prefs.layout_right_pct;
297 app.keymap = prefs.keymap.clone();
298 app.sort_mode = prefs.sort_mode;
299 app.package_marker = prefs.package_marker;
300 app.show_recent_pane = prefs.show_recent_pane;
301 app.show_install_pane = prefs.show_install_pane;
302 app.show_keybinds_footer = prefs.show_keybinds_footer;
303 app.search_normal_mode = prefs.search_startup_mode;
304 app.fuzzy_search_enabled = prefs.fuzzy_search;
305 app.installed_packages_mode = prefs.installed_packages_mode;
306 app.app_mode = if prefs.start_in_news {
307 crate::state::types::AppMode::News
308 } else {
309 crate::state::types::AppMode::Package
310 };
311 app.news_filter_show_arch_news = prefs.news_filter_show_arch_news;
312 app.news_filter_show_advisories = prefs.news_filter_show_advisories;
313 app.news_filter_show_pkg_updates = prefs.news_filter_show_pkg_updates;
314 app.news_filter_show_aur_updates = prefs.news_filter_show_aur_updates;
315 app.news_filter_show_aur_comments = prefs.news_filter_show_aur_comments;
316 app.news_filter_installed_only = prefs.news_filter_installed_only;
317 app.news_max_age_days = prefs.news_max_age_days;
318 app.refresh_news_results();
320}
321
322fn check_gnome_terminal(app: &mut AppState, headless: bool) {
334 if headless {
335 return;
336 }
337
338 let is_gnome = std::env::var("XDG_CURRENT_DESKTOP")
339 .ok()
340 .is_some_and(|v| v.to_uppercase().contains("GNOME"));
341
342 if !is_gnome {
343 return;
344 }
345
346 let has_gterm = crate::install::command_on_path("gnome-terminal");
347 let has_gconsole =
348 crate::install::command_on_path("gnome-console") || crate::install::command_on_path("kgx");
349
350 if !(has_gterm || has_gconsole) {
351 app.modal = crate::state::Modal::GnomeTerminalPrompt;
352 }
353}
354
355fn load_details_cache(app: &mut AppState) {
365 if let Ok(s) = std::fs::read_to_string(&app.cache_path)
366 && let Ok(map) = serde_json::from_str::<HashMap<String, PackageDetails>>(&s)
367 {
368 app.details_cache = map;
369 tracing::info!(path = %app.cache_path.display(), "loaded details cache");
370 }
371}
372
373fn load_recent_searches(app: &mut AppState) {
384 if let Ok(s) = std::fs::read_to_string(&app.recent_path)
385 && let Ok(list) = serde_json::from_str::<Vec<String>>(&s)
386 {
387 let count = list.len();
388 app.load_recent_items(&list);
389 if count > 0 {
390 app.history_state.select(Some(0));
391 }
392 tracing::info!(
393 path = %app.recent_path.display(),
394 count = count,
395 "loaded recent searches"
396 );
397 }
398}
399
400fn load_install_list(app: &mut AppState) {
411 if let Ok(s) = std::fs::read_to_string(&app.install_path)
412 && let Ok(list) = serde_json::from_str::<Vec<PackageItem>>(&s)
413 {
414 app.install_list = list;
415 if !app.install_list.is_empty() {
416 app.install_state.select(Some(0));
417 }
418 tracing::info!(
419 path = %app.install_path.display(),
420 count = app.install_list.len(),
421 "loaded install list"
422 );
423 }
424}
425
426fn load_news_read_urls(app: &mut AppState) {
436 if let Ok(s) = std::fs::read_to_string(&app.news_read_path)
437 && let Ok(set) = serde_json::from_str::<std::collections::HashSet<String>>(&s)
438 {
439 app.news_read_urls = set;
440 tracing::info!(
441 path = %app.news_read_path.display(),
442 count = app.news_read_urls.len(),
443 "loaded read news urls"
444 );
445 }
446}
447
448fn load_news_read_ids(app: &mut AppState) {
459 if let Ok(s) = std::fs::read_to_string(&app.news_read_ids_path)
460 && let Ok(set) = serde_json::from_str::<std::collections::HashSet<String>>(&s)
461 {
462 app.news_read_ids = set;
463 tracing::info!(
464 path = %app.news_read_ids_path.display(),
465 count = app.news_read_ids.len(),
466 "loaded read news ids"
467 );
468 return;
469 }
470
471 if app.news_read_ids.is_empty() && !app.news_read_urls.is_empty() {
472 app.news_read_ids.extend(app.news_read_urls.iter().cloned());
473 tracing::info!(
474 copied = app.news_read_ids.len(),
475 "seeded news read ids from legacy URL set"
476 );
477 app.news_read_ids_dirty = true;
478 }
479}
480
481fn load_announcement_state(app: &mut AppState) {
492 #[derive(serde::Deserialize)]
501 struct OldAnnouncementReadState {
502 hash: Option<String>,
504 }
505 if let Ok(s) = std::fs::read_to_string(&app.announcement_read_path) {
506 if let Ok(ids) = serde_json::from_str::<std::collections::HashSet<String>>(&s) {
508 app.announcements_read_ids = ids;
509 tracing::info!(
510 path = %app.announcement_read_path.display(),
511 count = app.announcements_read_ids.len(),
512 "loaded announcement read IDs"
513 );
514 return;
515 }
516 if let Ok(old_state) = serde_json::from_str::<OldAnnouncementReadState>(&s)
517 && let Some(hash) = old_state.hash
518 {
519 app.announcements_read_ids.insert(format!("hash:{hash}"));
520 app.announcement_dirty = true; tracing::info!(
522 path = %app.announcement_read_path.display(),
523 "migrated old announcement read state"
524 );
525 }
526 }
527}
528
529fn check_version_announcement(app: &mut AppState) {
540 const CURRENT_VERSION: &str = env!("CARGO_PKG_VERSION");
541
542 let current_base_version = crate::announcements::extract_base_version(CURRENT_VERSION);
544
545 if let Some(announcement) = crate::announcements::VERSION_ANNOUNCEMENTS
547 .iter()
548 .find(|a| {
549 let announcement_base_version = crate::announcements::extract_base_version(a.version);
550 announcement_base_version == current_base_version
551 })
552 {
553 let version_id = format!("v{CURRENT_VERSION}");
556
557 if app.announcements_read_ids.contains(&version_id) {
559 tracing::info!(
560 current_version = CURRENT_VERSION,
561 base_version = %current_base_version,
562 "version announcement already marked as read"
563 );
564 return;
565 }
566
567 app.modal = crate::state::Modal::Announcement {
569 title: announcement.title.to_string(),
570 content: announcement.content.to_string(),
571 id: version_id,
572 scroll: 0,
573 };
574 tracing::info!(
575 current_version = CURRENT_VERSION,
576 base_version = %current_base_version,
577 announcement_version = announcement.version,
578 "showing version announcement modal"
579 );
580 }
581 }
584
585pub fn initialize_app_state(
604 app: &mut AppState,
605 dry_run_flag: bool,
606 headless: bool,
607 prefs: &crate::theme::Settings,
608) -> InitFlags {
609 app.dry_run = if dry_run_flag {
610 true
611 } else {
612 prefs.app_dry_run_default
613 };
614 app.last_input_change = Instant::now();
615
616 tracing::info!(
618 recent = %app.recent_path.display(),
619 install = %app.install_path.display(),
620 details_cache = %app.cache_path.display(),
621 index = %app.official_index_path.display(),
622 news_read = %app.news_read_path.display(),
623 news_read_ids = %app.news_read_ids_path.display(),
624 announcement_read = %app.announcement_read_path.display(),
625 "resolved state file paths"
626 );
627
628 apply_settings_to_app_state(app, prefs);
629
630 initialize_locale_system(app, &prefs.locale, prefs);
632
633 check_gnome_terminal(app, headless);
634
635 if !headless && !prefs.startup_news_configured {
637 if matches!(app.modal, crate::state::Modal::None) {
639 app.modal = crate::state::Modal::NewsSetup {
640 show_arch_news: prefs.startup_news_show_arch_news,
641 show_advisories: prefs.startup_news_show_advisories,
642 show_aur_updates: prefs.startup_news_show_aur_updates,
643 show_aur_comments: prefs.startup_news_show_aur_comments,
644 show_pkg_updates: prefs.startup_news_show_pkg_updates,
645 max_age_days: prefs.startup_news_max_age_days,
646 cursor: 0,
647 };
648 }
649 } else if !headless && prefs.startup_news_configured {
650 app.news_loading = true;
653 app.toast_message = Some(crate::i18n::t(app, "app.news_button.loading"));
654 app.toast_expires_at = None; }
656
657 if !headless {
659 let username = std::env::var("USER").unwrap_or_else(|_| "user".to_string());
660 let (is_locked, lockout_until, remaining_minutes) =
661 crate::logic::faillock::get_lockout_info(&username);
662 app.faillock_locked = is_locked;
663 app.faillock_lockout_until = lockout_until;
664 app.faillock_remaining_minutes = remaining_minutes;
665 }
666
667 load_details_cache(app);
668 load_recent_searches(app);
669 load_install_list(app);
670 initialize_cache_files(app);
671
672 let (deps_cache, needs_deps_resolution) = load_cache_with_signature(
674 &app.install_list,
675 &app.deps_cache_path,
676 deps_cache::compute_signature,
677 deps_cache::load_cache,
678 "dependency",
679 );
680 if let Some(cached_deps) = deps_cache {
681 app.install_list_deps = cached_deps;
682 tracing::info!(
683 path = %app.deps_cache_path.display(),
684 count = app.install_list_deps.len(),
685 "loaded dependency cache"
686 );
687 }
688
689 let (files_cache, needs_files_resolution) = load_cache_with_signature(
691 &app.install_list,
692 &app.files_cache_path,
693 files_cache::compute_signature,
694 files_cache::load_cache,
695 "file",
696 );
697 if let Some(cached_files) = files_cache {
698 app.install_list_files = cached_files;
699 tracing::info!(
700 path = %app.files_cache_path.display(),
701 count = app.install_list_files.len(),
702 "loaded file cache"
703 );
704 }
705
706 let (services_cache, needs_services_resolution) = load_cache_with_signature(
708 &app.install_list,
709 &app.services_cache_path,
710 services_cache::compute_signature,
711 services_cache::load_cache,
712 "service",
713 );
714 if let Some(cached_services) = services_cache {
715 app.install_list_services = cached_services;
716 tracing::info!(
717 path = %app.services_cache_path.display(),
718 count = app.install_list_services.len(),
719 "loaded service cache"
720 );
721 }
722
723 let (sandbox_cache, needs_sandbox_resolution) = load_cache_with_signature(
725 &app.install_list,
726 &app.sandbox_cache_path,
727 sandbox_cache::compute_signature,
728 sandbox_cache::load_cache,
729 "sandbox",
730 );
731 if let Some(cached_sandbox) = sandbox_cache {
732 app.install_list_sandbox = cached_sandbox;
733 tracing::info!(
734 path = %app.sandbox_cache_path.display(),
735 count = app.install_list_sandbox.len(),
736 "loaded sandbox cache"
737 );
738 }
739
740 load_news_read_urls(app);
741 load_news_read_ids(app);
742 load_announcement_state(app);
743
744 pkgindex::load_from_disk(&app.official_index_path);
745
746 check_version_announcement(app);
748 tracing::info!(
749 path = %app.official_index_path.display(),
750 "attempted to load official index from disk"
751 );
752
753 InitFlags {
754 needs_deps_resolution,
755 needs_files_resolution,
756 needs_services_resolution,
757 needs_sandbox_resolution,
758 }
759}
760
761pub fn trigger_initial_resolutions(
777 app: &mut AppState,
778 flags: &InitFlags,
779 deps_req_tx: &tokio::sync::mpsc::UnboundedSender<(
780 Vec<PackageItem>,
781 crate::state::modal::PreflightAction,
782 )>,
783 files_req_tx: &tokio::sync::mpsc::UnboundedSender<(
784 Vec<PackageItem>,
785 crate::state::modal::PreflightAction,
786 )>,
787 services_req_tx: &tokio::sync::mpsc::UnboundedSender<(
788 Vec<PackageItem>,
789 crate::state::modal::PreflightAction,
790 )>,
791 sandbox_req_tx: &tokio::sync::mpsc::UnboundedSender<Vec<PackageItem>>,
792) {
793 if flags.needs_deps_resolution && !app.install_list.is_empty() {
794 app.deps_resolving = true;
795 let _ = deps_req_tx.send((
797 app.install_list.clone(),
798 crate::state::modal::PreflightAction::Install,
799 ));
800 }
801
802 if flags.needs_files_resolution && !app.install_list.is_empty() {
803 app.files_resolving = true;
804 let _ = files_req_tx.send((
806 app.install_list.clone(),
807 crate::state::modal::PreflightAction::Install,
808 ));
809 }
810
811 if flags.needs_services_resolution && !app.install_list.is_empty() {
812 app.services_resolving = true;
813 let _ = services_req_tx.send((
814 app.install_list.clone(),
815 crate::state::modal::PreflightAction::Install,
816 ));
817 }
818
819 if flags.needs_sandbox_resolution && !app.install_list.is_empty() {
820 app.sandbox_resolving = true;
821 let _ = sandbox_req_tx.send(app.install_list.clone());
822 }
823}
824
825#[cfg(test)]
826mod tests {
827 use super::*;
828 use crate::app::runtime::background::Channels;
829
830 fn new_app() -> AppState {
835 AppState::default()
836 }
837
838 #[test]
839 fn initialize_locale_system_fallback_when_config_missing() {
852 let mut app = new_app();
853 let prefs = crate::theme::Settings::default();
854
855 initialize_locale_system(&mut app, "", &prefs);
857
858 assert!(!app.locale.is_empty());
860 assert!(app.translations.is_empty() || !app.translations.is_empty());
862 assert!(app.translations_fallback.is_empty() || !app.translations_fallback.is_empty());
863 }
864
865 #[test]
866 fn initialize_app_state_sets_dry_run_flag() {
880 let mut app = new_app();
881 let prefs = crate::theme::settings();
882 let flags = initialize_app_state(&mut app, true, false, &prefs);
883
884 assert!(app.dry_run);
885 let _ = flags;
888 }
889
890 #[test]
891 fn initialize_app_state_loads_settings() {
906 let mut app = new_app();
907 let prefs = crate::theme::settings();
908 let _flags = initialize_app_state(&mut app, false, false, &prefs);
909
910 assert!(app.layout_left_pct > 0);
912 assert!(app.layout_center_pct > 0);
913 assert!(app.layout_right_pct > 0);
914 }
918
919 #[test]
920 fn initialize_cache_files_creates_empty_placeholders() {
931 let mut app = new_app();
932 let mut deps_path = std::env::temp_dir();
933 deps_path.push(format!(
934 "pacsea_init_deps_cache_{}_{}.json",
935 std::process::id(),
936 std::time::SystemTime::now()
937 .duration_since(std::time::UNIX_EPOCH)
938 .expect("System time is before UNIX epoch")
939 .as_nanos()
940 ));
941 let mut files_path = deps_path.clone();
942 files_path.set_file_name("pacsea_init_files_cache.json");
943 let mut services_path = deps_path.clone();
944 services_path.set_file_name("pacsea_init_services_cache.json");
945 let mut sandbox_path = deps_path.clone();
946 sandbox_path.set_file_name("pacsea_init_sandbox_cache.json");
947
948 app.deps_cache_path = deps_path.clone();
949 app.files_cache_path = files_path.clone();
950 app.services_cache_path = services_path.clone();
951 app.sandbox_cache_path = sandbox_path.clone();
952
953 let _ = std::fs::remove_file(&app.deps_cache_path);
955 let _ = std::fs::remove_file(&app.files_cache_path);
956 let _ = std::fs::remove_file(&app.services_cache_path);
957 let _ = std::fs::remove_file(&app.sandbox_cache_path);
958
959 initialize_cache_files(&app);
960
961 let deps_body = std::fs::read_to_string(&app.deps_cache_path)
962 .expect("Dependency cache file should exist");
963 let deps_cache: crate::app::deps_cache::DependencyCache =
964 serde_json::from_str(&deps_body).expect("Dependency cache should parse");
965 assert!(deps_cache.install_list_signature.is_empty());
966 assert!(deps_cache.dependencies.is_empty());
967
968 let files_body =
969 std::fs::read_to_string(&app.files_cache_path).expect("File cache file should exist");
970 let files_cache: crate::app::files_cache::FileCache =
971 serde_json::from_str(&files_body).expect("File cache should parse");
972 assert!(files_cache.install_list_signature.is_empty());
973 assert!(files_cache.files.is_empty());
974
975 let services_body = std::fs::read_to_string(&app.services_cache_path)
976 .expect("Service cache file should exist");
977 let services_cache: crate::app::services_cache::ServiceCache =
978 serde_json::from_str(&services_body).expect("Service cache should parse");
979 assert!(services_cache.install_list_signature.is_empty());
980 assert!(services_cache.services.is_empty());
981
982 let sandbox_body = std::fs::read_to_string(&app.sandbox_cache_path)
983 .expect("Sandbox cache file should exist");
984 let sandbox_cache: crate::app::sandbox_cache::SandboxCache =
985 serde_json::from_str(&sandbox_body).expect("Sandbox cache should parse");
986 assert!(sandbox_cache.install_list_signature.is_empty());
987 assert!(sandbox_cache.sandbox_info.is_empty());
988
989 let _ = std::fs::remove_file(&app.deps_cache_path);
990 let _ = std::fs::remove_file(&app.files_cache_path);
991 let _ = std::fs::remove_file(&app.services_cache_path);
992 let _ = std::fs::remove_file(&app.sandbox_cache_path);
993 }
994
995 #[tokio::test]
996 async fn trigger_initial_resolutions_skips_when_install_list_empty() {
1009 let mut app = new_app();
1010 app.install_list.clear();
1011
1012 let flags = InitFlags {
1013 needs_deps_resolution: true,
1014 needs_files_resolution: true,
1015 needs_services_resolution: true,
1016 needs_sandbox_resolution: true,
1017 };
1018
1019 let channels = Channels::new(std::path::PathBuf::from("/tmp"));
1021
1022 trigger_initial_resolutions(
1024 &mut app,
1025 &flags,
1026 &channels.deps_req_tx,
1027 &channels.files_req_tx,
1028 &channels.services_req_tx,
1029 &channels.sandbox_req_tx,
1030 );
1031
1032 assert!(!app.deps_resolving);
1034 assert!(!app.files_resolving);
1035 assert!(!app.services_resolving);
1036 assert!(!app.sandbox_resolving);
1037 }
1038
1039 #[tokio::test]
1040 async fn trigger_initial_resolutions_triggers_when_needed() {
1054 let mut app = new_app();
1055 app.install_list.push(crate::state::PackageItem {
1056 name: "test-package".to_string(),
1057 version: "1.0.0".to_string(),
1058 description: "Test".to_string(),
1059 source: crate::state::Source::Aur,
1060 popularity: None,
1061 out_of_date: None,
1062 orphaned: false,
1063 });
1064
1065 let flags = InitFlags {
1066 needs_deps_resolution: true,
1067 needs_files_resolution: false,
1068 needs_services_resolution: false,
1069 needs_sandbox_resolution: false,
1070 };
1071
1072 let channels = Channels::new(std::path::PathBuf::from("/tmp"));
1073
1074 trigger_initial_resolutions(
1075 &mut app,
1076 &flags,
1077 &channels.deps_req_tx,
1078 &channels.files_req_tx,
1079 &channels.services_req_tx,
1080 &channels.sandbox_req_tx,
1081 );
1082
1083 assert!(app.deps_resolving);
1085 assert!(!app.files_resolving);
1087 assert!(!app.services_resolving);
1088 assert!(!app.sandbox_resolving);
1089 }
1090}