| ... | @@ -825,3 +825,95 @@ fn remote_changes_after_review_cannot_be_overwritten_by_the_reviewed_page() { | ... | @@ -825,3 +825,95 @@ fn remote_changes_after_review_cannot_be_overwritten_by_the_reviewed_page() { |
| 825 | ); | 825 | ); |
| 826 | } | 826 | } |
| 827 | } | 827 | } |
| | 828 | |
| | 829 | #[test] |
| | 830 | fn remote_restore_retains_historical_receipts_without_replaying_them() { |
| | 831 | let directory = tempfile::tempdir().unwrap(); |
| | 832 | let path = directory.path().join("cache.sqlite"); |
| | 833 | let source = onestore::create_section("restore.one", "Original", "Fixture").unwrap(); |
| | 834 | let (_, object, _) = text(&source); |
| | 835 | let cache = Replica::create(&path, &source).unwrap(); |
| | 836 | let mut server = Server::new(&source); |
| | 837 | let published = save(&cache, object, 0..0, "Published ").unwrap(); |
| | 838 | let (_, receipt) = cache.sync_once(&mut server).unwrap().unwrap(); |
| | 839 | assert!(matches!(receipt, EditStatus::Published { .. })); |
| | 840 | let published_image = cache.snapshot().unwrap(); |
| | 841 | cache |
| | 842 | .export_recovery(directory.path().join("published.sqlite")) |
| | 843 | .unwrap(); |
| | 844 | drop(cache); |
| | 845 | |
| | 846 | server.visible.clone_from(&source); |
| | 847 | server.durable.clone_from(&source); |
| | 848 | let cache = Replica::open(&path).unwrap(); |
| | 849 | assert_eq!(cache.sync_once(&mut server).unwrap(), None); |
| | 850 | assert_eq!(cache.snapshot().unwrap(), source); |
| | 851 | assert_eq!(cache.remote_snapshot().unwrap(), source); |
| | 852 | assert_eq!(cache.status(published).unwrap(), Some(receipt)); |
| | 853 | assert_eq!(server.publications, 1); |
| | 854 | assert!(cache.pending().unwrap().is_empty()); |
| | 855 | let archive = notebook::Recovery::open(directory.path().join("published.sqlite")).unwrap(); |
| | 856 | assert_eq!(archive.snapshot().unwrap(), published_image); |
| | 857 | assert_eq!(archive.status(published).unwrap(), Some(receipt)); |
| | 858 | assert_eq!(text(&archive.snapshot().unwrap()).2, "Published Original"); |
| | 859 | drop(cache); |
| | 860 | let cache = Replica::open(path).unwrap(); |
| | 861 | assert_eq!(text(&cache.snapshot().unwrap()).2, "Original"); |
| | 862 | assert_eq!(cache.status(published).unwrap(), Some(receipt)); |
| | 863 | } |
| | 864 | |
| | 865 | #[test] |
| | 866 | fn remote_restore_rebases_unsent_work_but_never_replays_an_uncertain_attempt() { |
| | 867 | for fault in [Fault::None, Fault::UnknownBefore, Fault::UnknownAfter] { |
| | 868 | let uncertain = !matches!(fault, Fault::None); |
| | 869 | let directory = tempfile::tempdir().unwrap(); |
| | 870 | let path = directory.path().join("cache.sqlite"); |
| | 871 | let source = onestore::create_section("restore.one", "Original", "Fixture").unwrap(); |
| | 872 | let (_, object, _) = text(&source); |
| | 873 | let cache = Replica::create(&path, &source).unwrap(); |
| | 874 | let mut server = Server::new(&source); |
| | 875 | let published = save(&cache, object, 0..0, "Published ").unwrap(); |
| | 876 | let (_, receipt) = cache.sync_once(&mut server).unwrap().unwrap(); |
| | 877 | let end = u32::try_from(text(&cache.snapshot().unwrap()).2.encode_utf16().count()).unwrap(); |
| | 878 | let queued = save(&cache, object, end..end, " Local").unwrap(); |
| | 879 | if uncertain { |
| | 880 | server.fault = fault; |
| | 881 | assert!(cache.sync_once(&mut server).is_err()); |
| | 882 | } |
| | 883 | let prior_status = cache.status(queued).unwrap(); |
| | 884 | let local = cache.snapshot().unwrap(); |
| | 885 | let pending = cache.pending().unwrap(); |
| | 886 | cache |
| | 887 | .export_recovery(directory.path().join("before-restore.sqlite")) |
| | 888 | .unwrap(); |
| | 889 | drop(cache); |
| | 890 | server.visible.clone_from(&source); |
| | 891 | server.durable.clone_from(&source); |
| | 892 | let attempts = server.publications; |
| | 893 | let cache = Replica::open(&path).unwrap(); |
| | 894 | let (_, status) = cache.sync_once(&mut server).unwrap().unwrap(); |
| | 895 | assert_eq!(cache.status(published).unwrap(), Some(receipt)); |
| | 896 | if uncertain { |
| | 897 | assert!(matches!(status, EditStatus::AwaitingConfirmation { .. })); |
| | 898 | assert_eq!(Some(status), prior_status); |
| | 899 | assert_eq!(server.publications, attempts); |
| | 900 | assert_eq!(server.visible, source); |
| | 901 | assert_eq!(cache.snapshot().unwrap(), local); |
| | 902 | assert_eq!(cache.pending().unwrap(), pending); |
| | 903 | } else { |
| | 904 | assert!(matches!(status, EditStatus::Published { .. })); |
| | 905 | assert_eq!(server.publications, attempts + 1); |
| | 906 | assert_eq!(text(&server.visible).2, "Original Local"); |
| | 907 | assert!(cache.pending().unwrap().is_empty()); |
| | 908 | } |
| | 909 | let archive = |
| | 910 | notebook::Recovery::open(directory.path().join("before-restore.sqlite")).unwrap(); |
| | 911 | assert_eq!(archive.snapshot().unwrap(), local); |
| | 912 | assert_eq!(archive.pending().unwrap(), pending); |
| | 913 | assert_eq!(archive.status(queued).unwrap(), prior_status); |
| | 914 | drop(cache); |
| | 915 | let cache = Replica::open(&path).unwrap(); |
| | 916 | assert_eq!(cache.status(queued).unwrap(), Some(status)); |
| | 917 | assert_eq!(cache.status(published).unwrap(), Some(receipt)); |
| | 918 | } |
| | 919 | } |