use std::sync::{Arc, Mutex}; use remote_server::manager::RemoteServerManagerEvent;use warp_util::remote_path::RemotePath; use super::InternalRemoteDiffState;use crate::auth::AuthStateProvider;use crate::code_review::diff_size_limits::DiffSize;use crate::code_review::diff_state::{ DiffHunk, DiffLine, DiffLineType, DiffMetadata, DiffMetadataAgainstBase, DiffMode, DiffState, DiffStateModelEvent, DiffStats, FileDiff, FileDiffAndContent, GitDiffData, GitDiffWithBaseContent, GitFileStatus, RemoteDiffStateModel,};use crate::server::telemetry::context_provider::AppTelemetryContextProvider;use crate::util::git::Commit; impl RemoteDiffStateModel { fn new_for_test( mode: DiffMode, state: InternalRemoteDiffState, metadata: Option<DiffMetadata>, ) -> Self { Self { remote_path: RemotePath::new( remote_server::HostId::new("test-host".to_string()), warp_util::standardized_path::StandardizedPath::try_new("/test/repo") .expect("test repo path should be valid and absolute"), ), mode, state, metadata, tracked_diff_load_start_time: None, } }} fn empty_metadata(branch: &str) -> DiffMetadata { DiffMetadata { main_branch_name: "main".to_string(), current_branch_name: branch.to_string(), against_head: DiffMetadataAgainstBase { aggregate_stats: DiffStats::default(), files: vec![], }, against_base_branch: None, has_head_commit: true, unpushed_commits: vec![], upstream_ref: None, }} /// Snapshot inputs grouped for ergonomic test construction. Mirrors the/// argument list of `RemoteDiffStateModel::apply_snapshot`.struct SnapshotInputs { metadata: Option<DiffMetadata>, state: DiffState, diffs: Option<GitDiffWithBaseContent>,} fn loaded_snapshot_with_files(files: Vec<FileDiffAndContent>) -> SnapshotInputs { let files_changed = files.len(); SnapshotInputs { metadata: Some(empty_metadata("feature")), state: DiffState::Loaded, diffs: Some(GitDiffWithBaseContent { files, total_additions: 1, total_deletions: 0, files_changed, }), }} fn simple_file(path: &str) -> FileDiffAndContent { simple_file_with_content(path, None)} fn simple_file_with_content(path: &str, content_at_base: Option<&str>) -> FileDiffAndContent { FileDiffAndContent { file_diff: FileDiff { file_path: path.to_string(), status: GitFileStatus::Modified, hunks: Arc::new(vec![DiffHunk { old_start_line: 1, old_line_count: 1, new_start_line: 1, new_line_count: 2, lines: vec![DiffLine { line_type: DiffLineType::Add, old_line_number: None, new_line_number: Some(2), text: "+new line".to_string(), no_trailing_newline: false, }], unified_diff_start: 0, unified_diff_end: 1, }]), is_binary: false, is_autogenerated: false, max_line_number: 10, has_hidden_bidi_chars: false, size: DiffSize::Normal, }, content_at_head: content_at_base.map(str::to_string), }} fn test_metadata(branch: &str) -> DiffMetadata { DiffMetadata { main_branch_name: "main".to_string(), current_branch_name: branch.to_string(), against_head: DiffMetadataAgainstBase { aggregate_stats: DiffStats { files_changed: 1, total_additions: 5, total_deletions: 2, }, files: vec![], }, against_base_branch: None, has_head_commit: true, unpushed_commits: vec![Commit { hash: "abc123".to_string(), subject: "test commit".to_string(), files_changed: 1, additions: 5, deletions: 2, files: vec![], }], upstream_ref: Some("origin/feature".to_string()), }} fn initialize_test_app(app: &mut warpui::App) { app.add_singleton_model(|_| AuthStateProvider::new_for_test()); app.add_singleton_model(AppTelemetryContextProvider::new_context_provider);}#[test]fn apply_snapshot_loaded_with_diffs() { warpui::App::test((), |mut app| async move { let handle = app.add_model(|_ctx| { RemoteDiffStateModel::new_for_test( DiffMode::Head, InternalRemoteDiffState::Loading, None, ) }); let SnapshotInputs { metadata, state, diffs, } = loaded_snapshot_with_files(vec![simple_file("src/main.rs")]); handle.update(&mut app, |m, ctx| { m.apply_snapshot(metadata, state, diffs, ctx) }); handle.read(&app, |m, _| { assert!(matches!(m.get(), DiffState::Loaded)); assert!(m.metadata.is_some()); }); });} #[test]fn get_committed_branch_files_response_emits_domain_files() { warpui::App::test((), |mut app| async move { let handle = app.add_model(|_ctx| { RemoteDiffStateModel::new_for_test( DiffMode::Head, InternalRemoteDiffState::Loading, None, ) }); let emitted = Arc::new(Mutex::new(Vec::new())); { let emitted = emitted.clone(); app.update(|ctx| { ctx.subscribe_to_model(&handle, move |_, event, _| { if let DiffStateModelEvent::BranchCommittedFilesReceived(files) = event { emitted .lock() .expect("emitted mutex should not be poisoned") .push(files.clone()); } }); }); } // Success: proto entries are converted to domain entries and emitted. let proto_files = vec![remote_server::proto::FileChangeEntry { path: "src/main.rs".to_string(), additions: 3, deletions: 1, }]; handle.update(&mut app, |m, ctx| { m.handle_get_committed_branch_files_response(&Ok(proto_files), ctx); }); // Error: an empty list is emitted so the dialog shows an empty box // rather than stale data. handle.update(&mut app, |m, ctx| { m.handle_get_committed_branch_files_response(&Err("boom".to_string()), ctx); }); let emitted = emitted .lock() .expect("emitted mutex should not be poisoned"); assert_eq!(emitted.len(), 2); assert_eq!(emitted[0].len(), 1); assert_eq!(emitted[0][0].path, "src/main.rs"); assert_eq!(emitted[0][0].additions, 3); assert_eq!(emitted[0][0].deletions, 1); assert!(emitted[1].is_empty()); });} #[test]fn apply_snapshot_loaded_preserves_content_at_base_in_event() { warpui::App::test((), |mut app| async move { let handle = app.add_model(|_ctx| { RemoteDiffStateModel::new_for_test( DiffMode::Head, InternalRemoteDiffState::Loading, None, ) }); let emitted_content = Arc::new(Mutex::new(Vec::new())); { let emitted_content = emitted_content.clone(); app.update(|ctx| { ctx.subscribe_to_model(&handle, move |_, event, _| { if let DiffStateModelEvent::NewDiffsComputed { diffs: Some(diffs), .. } = event { emitted_content .lock() .expect("emitted content mutex should not be poisoned") .push( diffs .files .first() .and_then(|file| file.content_at_head.clone()), ); } }); }); } let SnapshotInputs { metadata, state, diffs, } = loaded_snapshot_with_files(vec![simple_file_with_content( "src/main.rs", Some("base content"), )]); handle.update(&mut app, |m, ctx| { m.apply_snapshot(metadata, state, diffs, ctx) }); assert_eq!( emitted_content .lock() .expect("emitted content mutex should not be poisoned") .as_slice(), &[Some("base content".to_string())] ); });} #[test]fn apply_snapshot_loaded_without_diffs_becomes_error() { warpui::App::test((), |mut app| async move { initialize_test_app(&mut app); let handle = app.add_model(|_ctx| { RemoteDiffStateModel::new_for_test( DiffMode::Head, InternalRemoteDiffState::Loading, None, ) }); handle.update(&mut app, |m, ctx| { m.apply_snapshot(None, DiffState::Loaded, None, ctx) }); assert!(matches!( handle.read(&app, |m, _| m.get()), DiffState::Error(_) )); });} #[test]fn apply_snapshot_not_in_repository() { warpui::App::test((), |mut app| async move { let handle = app.add_model(|_ctx| { RemoteDiffStateModel::new_for_test( DiffMode::Head, InternalRemoteDiffState::Loading, None, ) }); handle.update(&mut app, |m, ctx| { m.apply_snapshot(None, DiffState::NotInRepository, None, ctx) }); assert!(matches!( handle.read(&app, |m, _| m.get()), DiffState::NotInRepository )); });} #[test]fn apply_snapshot_error_stores_message() { warpui::App::test((), |mut app| async move { initialize_test_app(&mut app); let handle = app.add_model(|_ctx| { RemoteDiffStateModel::new_for_test( DiffMode::Head, InternalRemoteDiffState::Loading, None, ) }); handle.update(&mut app, |m, ctx| { m.apply_snapshot(None, DiffState::Error("git failed".to_string()), None, ctx) }); assert!( matches!(handle.read(&app, |m, _| m.get()), DiffState::Error(ref msg) if msg == "git failed") ); });} #[test]fn mark_disconnected_transitions_state_and_emits_connection_lost() { warpui::App::test((), |mut app| async move { let handle = app.add_model(|_ctx| { RemoteDiffStateModel::new_for_test( DiffMode::Head, InternalRemoteDiffState::Loading, None, ) }); let connection_lost_count = Arc::new(Mutex::new(0)); { let connection_lost_count = connection_lost_count.clone(); app.update(|ctx| { ctx.subscribe_to_model(&handle, move |_, event, _| { if matches!(event, DiffStateModelEvent::ConnectionLost) { *connection_lost_count .lock() .expect("connection lost count mutex should not be poisoned") += 1; } }); }); } handle.update(&mut app, |m, ctx| m.mark_disconnected(ctx)); handle.read(&app, |m, _| { assert!(matches!(m.get(), DiffState::Disconnected)); }); assert_eq!( *connection_lost_count .lock() .expect("connection lost count mutex should not be poisoned"), 1 ); // Idempotent: a second call should not re-emit ConnectionLost. handle.update(&mut app, |m, ctx| m.mark_disconnected(ctx)); assert_eq!( *connection_lost_count .lock() .expect("connection lost count mutex should not be poisoned"), 1 ); });} #[test]fn apply_metadata_first_time_sets_branch() { warpui::App::test((), |mut app| async move { let handle = app.add_model(|_ctx| { RemoteDiffStateModel::new_for_test( DiffMode::Head, InternalRemoteDiffState::Loading, None, ) }); let meta = empty_metadata("feature"); handle.update(&mut app, |m, ctx| m.apply_metadata_update(&meta, ctx)); assert_eq!( handle .read(&app, |m, _| m.get_current_branch_name()) .as_deref(), Some("feature") ); });} #[test]fn apply_metadata_branch_change_updates_branch() { warpui::App::test((), |mut app| async move { let handle = app.add_model(|_ctx| { RemoteDiffStateModel::new_for_test( DiffMode::Head, InternalRemoteDiffState::Loading, Some(test_metadata("feature-a")), ) }); let meta = empty_metadata("feature-b"); handle.update(&mut app, |m, ctx| m.apply_metadata_update(&meta, ctx)); assert_eq!( handle .read(&app, |m, _| m.get_current_branch_name()) .as_deref(), Some("feature-b") ); });} #[test]fn apply_file_delta_ignored_when_not_loaded() { warpui::App::test((), |mut app| async move { let handle = app.add_model(|_ctx| { RemoteDiffStateModel::new_for_test( DiffMode::Head, InternalRemoteDiffState::Loading, None, ) }); let file_path = "src/main.rs".to_string(); let diff = Some(simple_file("src/main.rs")); handle.update(&mut app, |m, ctx| { m.apply_file_delta(file_path, diff, None, ctx) }); assert!(matches!( handle.read(&app, |m, _| m.get()), DiffState::Loading )); });} #[test]fn apply_file_delta_adds_file() { warpui::App::test((), |mut app| async move { let handle = app.add_model(|_ctx| { RemoteDiffStateModel::new_for_test( DiffMode::Head, InternalRemoteDiffState::Loaded(GitDiffData { files: vec![], total_additions: 0, total_deletions: 0, files_changed: 0, }), None, ) }); let file_path = "src/new.rs".to_string(); let diff = Some(simple_file("src/new.rs")); handle.update(&mut app, |m, ctx| { m.apply_file_delta(file_path, diff, None, ctx) }); assert!(matches!( handle.read(&app, |m, _| m.get()), DiffState::Loaded )); });} #[test]fn apply_snapshot_preserves_repo_relative_file_paths() { warpui::App::test((), |mut app| async move { let handle = app.add_model(|_ctx| { RemoteDiffStateModel::new_for_test( DiffMode::Head, InternalRemoteDiffState::Loading, None, ) }); let SnapshotInputs { metadata, state, diffs, } = loaded_snapshot_with_files(vec![simple_file("src/main.rs")]); handle.update(&mut app, |m, ctx| { m.apply_snapshot(metadata, state, diffs, ctx) }); handle.read(&app, |m, _| { let InternalRemoteDiffState::Loaded(diffs) = &m.state else { panic!("state should be Loaded"); }; assert_eq!(diffs.files.len(), 1); assert_eq!(diffs.files[0].file_path, "src/main.rs"); }); });} #[test]fn apply_snapshot_emits_event_with_repo_relative_paths() { // Subscribers to NewDiffsComputed should see repo-relative paths so theyLines 1–500 of 915. Load more
use std::sync::{Arc, Mutex}; use remote_server::manager::RemoteServerManagerEvent; use warp_util::remote_path::RemotePath; use super::InternalRemoteDiffState; use crate::auth::AuthStateProvider; use crate::code_review::diff_size_limits::DiffSize; use crate::code_review::diff_state::{ DiffHunk, DiffLine, DiffLineType, DiffMetadata, DiffMetadataAgainstBase, DiffMode, DiffState, DiffStateModelEvent, DiffStats, FileDiff, FileDiffAndContent, GitDiffData, GitDiffWithBaseContent, GitFileStatus, RemoteDiffStateModel, }; use crate::server::telemetry::context_provider::AppTelemetryContextProvider; use crate::util::git::Commit; impl RemoteDiffStateModel { fn new_for_test( mode: DiffMode, state: InternalRemoteDiffState, metadata: Option<DiffMetadata>, ) -> Self { Self { remote_path: RemotePath::new( remote_server::HostId::new("test-host".to_string()), warp_util::standardized_path::StandardizedPath::try_new("/test/repo") .expect("test repo path should be valid and absolute"), ), mode, state, metadata, tracked_diff_load_start_time: None, } } } fn empty_metadata(branch: &str) -> DiffMetadata { DiffMetadata { main_branch_name: "main".to_string(), current_branch_name: branch.to_string(), against_head: DiffMetadataAgainstBase { aggregate_stats: DiffStats::default(), files: vec![], }, against_base_branch: None, has_head_commit: true, unpushed_commits: vec![], upstream_ref: None, } } /// Snapshot inputs grouped for ergonomic test construction. Mirrors the /// argument list of `RemoteDiffStateModel::apply_snapshot`. struct SnapshotInputs { metadata: Option<DiffMetadata>, state: DiffState, diffs: Option<GitDiffWithBaseContent>, } fn loaded_snapshot_with_files(files: Vec<FileDiffAndContent>) -> SnapshotInputs { let files_changed = files.len(); SnapshotInputs { metadata: Some(empty_metadata("feature")), state: DiffState::Loaded, diffs: Some(GitDiffWithBaseContent { files, total_additions: 1, total_deletions: 0, files_changed, }), } } fn simple_file(path: &str) -> FileDiffAndContent { simple_file_with_content(path, None) } fn simple_file_with_content(path: &str, content_at_base: Option<&str>) -> FileDiffAndContent { FileDiffAndContent { file_diff: FileDiff { file_path: path.to_string(), status: GitFileStatus::Modified, hunks: Arc::new(vec![DiffHunk { old_start_line: 1, old_line_count: 1, new_start_line: 1, new_line_count: 2, lines: vec![DiffLine { line_type: DiffLineType::Add, old_line_number: None, new_line_number: Some(2), text: "+new line".to_string(), no_trailing_newline: false, }], unified_diff_start: 0, unified_diff_end: 1, }]), is_binary: false, is_autogenerated: false, max_line_number: 10, has_hidden_bidi_chars: false, size: DiffSize::Normal, }, content_at_head: content_at_base.map(str::to_string), } } fn test_metadata(branch: &str) -> DiffMetadata { DiffMetadata { main_branch_name: "main".to_string(), current_branch_name: branch.to_string(), against_head: DiffMetadataAgainstBase { aggregate_stats: DiffStats { files_changed: 1, total_additions: 5, total_deletions: 2, }, files: vec![], }, against_base_branch: None, has_head_commit: true, unpushed_commits: vec![Commit { hash: "abc123".to_string(), subject: "test commit".to_string(), files_changed: 1, additions: 5, deletions: 2, files: vec![], }], upstream_ref: Some("origin/feature".to_string()), } } fn initialize_test_app(app: &mut warpui::App) { app.add_singleton_model(|_| AuthStateProvider::new_for_test()); app.add_singleton_model(AppTelemetryContextProvider::new_context_provider); } #[test] fn apply_snapshot_loaded_with_diffs() { warpui::App::test((), |mut app| async move { let handle = app.add_model(|_ctx| { RemoteDiffStateModel::new_for_test( DiffMode::Head, InternalRemoteDiffState::Loading, None, ) }); let SnapshotInputs { metadata, state, diffs, } = loaded_snapshot_with_files(vec![simple_file("src/main.rs")]); handle.update(&mut app, |m, ctx| { m.apply_snapshot(metadata, state, diffs, ctx) }); handle.read(&app, |m, _| { assert!(matches!(m.get(), DiffState::Loaded)); assert!(m.metadata.is_some()); }); }); } #[test] fn get_committed_branch_files_response_emits_domain_files() { warpui::App::test((), |mut app| async move { let handle = app.add_model(|_ctx| { RemoteDiffStateModel::new_for_test( DiffMode::Head, InternalRemoteDiffState::Loading, None, ) }); let emitted = Arc::new(Mutex::new(Vec::new())); { let emitted = emitted.clone(); app.update(|ctx| { ctx.subscribe_to_model(&handle, move |_, event, _| { if let DiffStateModelEvent::BranchCommittedFilesReceived(files) = event { emitted .lock() .expect("emitted mutex should not be poisoned") .push(files.clone()); } }); }); } // Success: proto entries are converted to domain entries and emitted. let proto_files = vec![remote_server::proto::FileChangeEntry { path: "src/main.rs".to_string(), additions: 3, deletions: 1, }]; handle.update(&mut app, |m, ctx| { m.handle_get_committed_branch_files_response(&Ok(proto_files), ctx); }); // Error: an empty list is emitted so the dialog shows an empty box // rather than stale data. handle.update(&mut app, |m, ctx| { m.handle_get_committed_branch_files_response(&Err("boom".to_string()), ctx); }); let emitted = emitted .lock() .expect("emitted mutex should not be poisoned"); assert_eq!(emitted.len(), 2); assert_eq!(emitted[0].len(), 1); assert_eq!(emitted[0][0].path, "src/main.rs"); assert_eq!(emitted[0][0].additions, 3); assert_eq!(emitted[0][0].deletions, 1); assert!(emitted[1].is_empty()); }); } #[test] fn apply_snapshot_loaded_preserves_content_at_base_in_event() { warpui::App::test((), |mut app| async move { let handle = app.add_model(|_ctx| { RemoteDiffStateModel::new_for_test( DiffMode::Head, InternalRemoteDiffState::Loading, None, ) }); let emitted_content = Arc::new(Mutex::new(Vec::new())); { let emitted_content = emitted_content.clone(); app.update(|ctx| { ctx.subscribe_to_model(&handle, move |_, event, _| { if let DiffStateModelEvent::NewDiffsComputed { diffs: Some(diffs), .. } = event { emitted_content .lock() .expect("emitted content mutex should not be poisoned") .push( diffs .files .first() .and_then(|file| file.content_at_head.clone()), ); } }); }); } let SnapshotInputs { metadata, state, diffs, } = loaded_snapshot_with_files(vec![simple_file_with_content( "src/main.rs", Some("base content"), )]); handle.update(&mut app, |m, ctx| { m.apply_snapshot(metadata, state, diffs, ctx) }); assert_eq!( emitted_content .lock() .expect("emitted content mutex should not be poisoned") .as_slice(), &[Some("base content".to_string())] ); }); } #[test] fn apply_snapshot_loaded_without_diffs_becomes_error() { warpui::App::test((), |mut app| async move { initialize_test_app(&mut app); let handle = app.add_model(|_ctx| { RemoteDiffStateModel::new_for_test( DiffMode::Head, InternalRemoteDiffState::Loading, None, ) }); handle.update(&mut app, |m, ctx| { m.apply_snapshot(None, DiffState::Loaded, None, ctx) }); assert!(matches!( handle.read(&app, |m, _| m.get()), DiffState::Error(_) )); }); } #[test] fn apply_snapshot_not_in_repository() { warpui::App::test((), |mut app| async move { let handle = app.add_model(|_ctx| { RemoteDiffStateModel::new_for_test( DiffMode::Head, InternalRemoteDiffState::Loading, None, ) }); handle.update(&mut app, |m, ctx| { m.apply_snapshot(None, DiffState::NotInRepository, None, ctx) }); assert!(matches!( handle.read(&app, |m, _| m.get()), DiffState::NotInRepository )); }); } #[test] fn apply_snapshot_error_stores_message() { warpui::App::test((), |mut app| async move { initialize_test_app(&mut app); let handle = app.add_model(|_ctx| { RemoteDiffStateModel::new_for_test( DiffMode::Head, InternalRemoteDiffState::Loading, None, ) }); handle.update(&mut app, |m, ctx| { m.apply_snapshot(None, DiffState::Error("git failed".to_string()), None, ctx) }); assert!( matches!(handle.read(&app, |m, _| m.get()), DiffState::Error(ref msg) if msg == "git failed") ); }); } #[test] fn mark_disconnected_transitions_state_and_emits_connection_lost() { warpui::App::test((), |mut app| async move { let handle = app.add_model(|_ctx| { RemoteDiffStateModel::new_for_test( DiffMode::Head, InternalRemoteDiffState::Loading, None, ) }); let connection_lost_count = Arc::new(Mutex::new(0)); { let connection_lost_count = connection_lost_count.clone(); app.update(|ctx| { ctx.subscribe_to_model(&handle, move |_, event, _| { if matches!(event, DiffStateModelEvent::ConnectionLost) { *connection_lost_count .lock() .expect("connection lost count mutex should not be poisoned") += 1; } }); }); } handle.update(&mut app, |m, ctx| m.mark_disconnected(ctx)); handle.read(&app, |m, _| { assert!(matches!(m.get(), DiffState::Disconnected)); }); assert_eq!( *connection_lost_count .lock() .expect("connection lost count mutex should not be poisoned"), 1 ); // Idempotent: a second call should not re-emit ConnectionLost. handle.update(&mut app, |m, ctx| m.mark_disconnected(ctx)); assert_eq!( *connection_lost_count .lock() .expect("connection lost count mutex should not be poisoned"), 1 ); }); } #[test] fn apply_metadata_first_time_sets_branch() { warpui::App::test((), |mut app| async move { let handle = app.add_model(|_ctx| { RemoteDiffStateModel::new_for_test( DiffMode::Head, InternalRemoteDiffState::Loading, None, ) }); let meta = empty_metadata("feature"); handle.update(&mut app, |m, ctx| m.apply_metadata_update(&meta, ctx)); assert_eq!( handle .read(&app, |m, _| m.get_current_branch_name()) .as_deref(), Some("feature") ); }); } #[test] fn apply_metadata_branch_change_updates_branch() { warpui::App::test((), |mut app| async move { let handle = app.add_model(|_ctx| { RemoteDiffStateModel::new_for_test( DiffMode::Head, InternalRemoteDiffState::Loading, Some(test_metadata("feature-a")), ) }); let meta = empty_metadata("feature-b"); handle.update(&mut app, |m, ctx| m.apply_metadata_update(&meta, ctx)); assert_eq!( handle .read(&app, |m, _| m.get_current_branch_name()) .as_deref(), Some("feature-b") ); }); } #[test] fn apply_file_delta_ignored_when_not_loaded() { warpui::App::test((), |mut app| async move { let handle = app.add_model(|_ctx| { RemoteDiffStateModel::new_for_test( DiffMode::Head, InternalRemoteDiffState::Loading, None, ) }); let file_path = "src/main.rs".to_string(); let diff = Some(simple_file("src/main.rs")); handle.update(&mut app, |m, ctx| { m.apply_file_delta(file_path, diff, None, ctx) }); assert!(matches!( handle.read(&app, |m, _| m.get()), DiffState::Loading )); }); } #[test] fn apply_file_delta_adds_file() { warpui::App::test((), |mut app| async move { let handle = app.add_model(|_ctx| { RemoteDiffStateModel::new_for_test( DiffMode::Head, InternalRemoteDiffState::Loaded(GitDiffData { files: vec![], total_additions: 0, total_deletions: 0, files_changed: 0, }), None, ) }); let file_path = "src/new.rs".to_string(); let diff = Some(simple_file("src/new.rs")); handle.update(&mut app, |m, ctx| { m.apply_file_delta(file_path, diff, None, ctx) }); assert!(matches!( handle.read(&app, |m, _| m.get()), DiffState::Loaded )); }); } #[test] fn apply_snapshot_preserves_repo_relative_file_paths() { warpui::App::test((), |mut app| async move { let handle = app.add_model(|_ctx| { RemoteDiffStateModel::new_for_test( DiffMode::Head, InternalRemoteDiffState::Loading, None, ) }); let SnapshotInputs { metadata, state, diffs, } = loaded_snapshot_with_files(vec![simple_file("src/main.rs")]); handle.update(&mut app, |m, ctx| { m.apply_snapshot(metadata, state, diffs, ctx) }); handle.read(&app, |m, _| { let InternalRemoteDiffState::Loaded(diffs) = &m.state else { panic!("state should be Loaded"); }; assert_eq!(diffs.files.len(), 1); assert_eq!(diffs.files[0].file_path, "src/main.rs"); }); }); } #[test] fn apply_snapshot_emits_event_with_repo_relative_paths() { // Subscribers to NewDiffsComputed should see repo-relative paths so they