app

Local-first trade for farms and co-ops
git clone https://radroots.dev/git/app.git
Log | Files | Refs | README | LICENSE

commit 27f8b81d56ebee7cc79ce16c4dbc3f5a8559a168
parent 9c19d6dce13dc9cf6c5f8b1e418483fbd5de5540
Author: triesap <tyson@radroots.org>
Date:   Fri, 17 Apr 2026 21:15:01 +0000

tests: cover today runtime state and source guards

Diffstat:
Mcrates/launchers/desktop/src/runtime.rs | 96+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Mcrates/launchers/desktop/src/source_guards.rs | 2+-
Mcrates/launchers/desktop/src/window.rs | 2+-
3 files changed, 84 insertions(+), 16 deletions(-)

diff --git a/crates/launchers/desktop/src/runtime.rs b/crates/launchers/desktop/src/runtime.rs @@ -136,13 +136,17 @@ mod tests { use std::path::PathBuf; use radroots_app_core::{AppRuntimeHostEnvironment, AppRuntimePlatform, AppRuntimeRoots}; - use radroots_app_models::TodayAgendaProjection; - use radroots_app_state::{AppStateStore, InMemoryAppStateRepository, SettingsPreference}; - - use super::{ - APP_DATABASE_FILE_NAME, DesktopAppRuntime, DesktopAppRuntimeState, SettingsSection, + use radroots_app_models::{ + AppMode, FarmReadiness, FarmSummary, SettingsSection, ShellSection, TodayAgendaProjection, + TodaySetupTask, TodaySetupTaskKind, TodaySummary, + }; + use radroots_app_state::{ + AppStateRepositoryError, AppStateStore, AppStateStoreError, InMemoryAppStateRepository, + SettingsPreference, }; + use super::{APP_DATABASE_FILE_NAME, DesktopAppRuntime, DesktopAppRuntimeState}; + #[test] fn desktop_namespace_uses_canonical_app_data_root() { let roots = AppRuntimeRoots::for_desktop( @@ -169,7 +173,7 @@ mod tests { } #[test] - fn cloned_runtime_handles_share_shell_state() { + fn cloned_runtime_handles_shared_settings_state() { let runtime = DesktopAppRuntime::from_state(DesktopAppRuntimeState { state_store: AppStateStore::load(InMemoryAppStateRepository::default()) .expect("in-memory state store should load"), @@ -179,28 +183,92 @@ mod tests { assert!(runtime.select_settings_section(SettingsSection::About)); assert!(cloned_runtime.set_settings_preference(SettingsPreference::LaunchAtLogin, true)); - assert!(cloned_runtime.replace_today_agenda(TodayAgendaProjection { - setup_checklist: vec![radroots_app_models::TodaySetupTask { - kind: radroots_app_models::TodaySetupTaskKind::AddFulfillmentWindow, + + let summary = runtime.summary(); + + assert_eq!( + summary.shell_projection.selected_section, + ShellSection::Home + ); + assert_eq!( + summary.shell_projection.settings.selected_section, + SettingsSection::About + ); + assert!(summary.shell_projection.settings.general.launch_at_login); + assert_eq!( + cloned_runtime.selected_settings_section(), + SettingsSection::About + ); + } + + #[test] + fn replacing_today_agenda_is_shared_without_clobbering_home_shell() { + let runtime = DesktopAppRuntime::from_state(DesktopAppRuntimeState { + state_store: AppStateStore::load(InMemoryAppStateRepository::default()) + .expect("in-memory state store should load"), + startup_issue: None, + }); + let cloned_runtime = runtime.clone(); + let today_agenda = TodayAgendaProjection { + farm: Some(FarmSummary { + farm_id: radroots_app_models::FarmId::new(), + display_name: "North field farm".to_owned(), + readiness: FarmReadiness::Incomplete, + }), + summary: Some(TodaySummary { + farm_id: radroots_app_models::FarmId::new(), + orders_needing_action: 2, + low_stock_products: 1, + draft_products: 3, + }), + setup_checklist: vec![TodaySetupTask { + kind: TodaySetupTaskKind::AddFulfillmentWindow, is_complete: false, }], ..TodayAgendaProjection::default() - })); + }; + + assert!(runtime.select_settings_section(SettingsSection::About)); + assert!(cloned_runtime.replace_today_agenda(today_agenda.clone())); let summary = runtime.summary(); + + assert_eq!(summary.today_projection, today_agenda); + assert_eq!(summary.shell_projection.app_mode, AppMode::Farmer); assert_eq!( summary.shell_projection.selected_section, - radroots_app_models::ShellSection::Home + ShellSection::Home ); assert_eq!( summary.shell_projection.settings.selected_section, SettingsSection::About ); - assert!(summary.shell_projection.settings.general.launch_at_login); assert!(summary.today_projection.needs_setup()); + } + + #[test] + fn degraded_runtime_surfaces_startup_issue_with_default_today_projection() { + let runtime = DesktopAppRuntime::from_state(DesktopAppRuntimeState::degraded( + super::DesktopAppRuntimeBootstrapError::State(AppStateStoreError::Repository( + AppStateRepositoryError::load("state unavailable"), + )), + )); + + let summary = runtime.summary(); + + assert_eq!(summary.shell_projection.app_mode, AppMode::Farmer); assert_eq!( - cloned_runtime.selected_settings_section(), - SettingsSection::About + summary.shell_projection.selected_section, + ShellSection::Home + ); + assert_eq!( + summary.shell_projection.settings.selected_section, + SettingsSection::Account + ); + assert_eq!(summary.today_projection, TodayAgendaProjection::default()); + assert_eq!( + summary.startup_issue.as_deref(), + Some("app state repository load failed: state unavailable") ); } } diff --git a/crates/launchers/desktop/src/source_guards.rs b/crates/launchers/desktop/src/source_guards.rs @@ -7,7 +7,7 @@ const ALLOWED_WINDOW_LITERALS: &[&str] = &[ "account-admin-console", "account-log-out", "account-more", - "home-metadata-scroll", + "home-today-scroll", "settings window should open", "settings-allow-relay-connections", "settings-launch-at-login", diff --git a/crates/launchers/desktop/src/window.rs b/crates/launchers/desktop/src/window.rs @@ -147,7 +147,7 @@ impl Render for HomeView { .p(px(APP_UI_THEME.layout.home_window_padding_px)) .child( div() - .id("home-metadata-scroll") + .id("home-today-scroll") .size_full() .overflow_y_scroll() .child(home_view_content(&runtime_summary)),