@tutao/node-mimimi 271.250224.0 → 271.250227.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Cargo.lock CHANGED
@@ -516,6 +516,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
516
516
  checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
517
517
  dependencies = [
518
518
  "powerfmt",
519
+ "serde",
519
520
  ]
520
521
 
521
522
  [[package]]
@@ -2172,6 +2173,7 @@ dependencies = [
2172
2173
  "pqcrypto-mlkem",
2173
2174
  "pqcrypto-traits",
2174
2175
  "rand_core",
2176
+ "regex",
2175
2177
  "rsa",
2176
2178
  "rustls",
2177
2179
  "serde",
@@ -2181,6 +2183,7 @@ dependencies = [
2181
2183
  "sha3",
2182
2184
  "simple_logger",
2183
2185
  "thiserror 2.0.11",
2186
+ "time",
2184
2187
  "tokio",
2185
2188
  "uniffi",
2186
2189
  "zeroize",
package/Cargo.toml CHANGED
@@ -13,6 +13,7 @@ crate-type = ["cdylib"]
13
13
  default = ["javascript"]
14
14
  # needed to turn off the autogenerated ffi when using the tests
15
15
  javascript = ["dep:napi-derive"]
16
+ test-with-local-http-server = []
16
17
 
17
18
  [dependencies]
18
19
  # Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tutao/node-mimimi",
3
- "version": "271.250224.0",
3
+ "version": "271.250227.0",
4
4
  "main": "./dist/binding.js",
5
5
  "types": "./dist/binding.d.ts",
6
6
  "napi": {
@@ -13,7 +13,7 @@
13
13
  },
14
14
  "license": "MIT",
15
15
  "devDependencies": {
16
- "@tutao/otest": "271.250224.0",
16
+ "@tutao/otest": "271.250227.0",
17
17
  "@napi-rs/cli": "3.0.0-alpha.68",
18
18
  "typescript": "5.3.3",
19
19
  "zx": "8.1.5"
@@ -30,8 +30,8 @@
30
30
  "version": "napi version"
31
31
  },
32
32
  "optionalDependencies": {
33
- "@tutao/node-mimimi-win32-x64-msvc": "271.250224.0",
34
- "@tutao/node-mimimi-linux-x64-gnu": "271.250224.0",
35
- "@tutao/node-mimimi-darwin-universal": "271.250224.0"
33
+ "@tutao/node-mimimi-win32-x64-msvc": "271.250227.0",
34
+ "@tutao/node-mimimi-linux-x64-gnu": "271.250227.0",
35
+ "@tutao/node-mimimi-darwin-universal": "271.250227.0"
36
36
  }
37
37
  }
package/src/importer.rs CHANGED
@@ -627,10 +627,10 @@ impl Importer {
627
627
  Ok(())
628
628
  }
629
629
 
630
- /// Decide weather this error should abort the import process, or it should move to import next chunk,
631
- /// - if it returns `Ok`, we should continue with the next chunk, if it returns `Err`, the error should
632
- /// be propagated to the node process to maybe be displayed and the import should stop for now.
633
- /// - if mails involved in this error should not be picked when user retry, move them to FAILED_MAILS_SUB_DIR
630
+ /// Decide weather this error should abort the import process, or it should move to import the next chunk.
631
+ /// - If it returns `Ok`, we should continue with the next chunk, if it returns `Err`, the error should
632
+ /// be propagated to the node process to maybe be displayed, and the import should stop for now.
633
+ /// - If mails involved in this error should not be picked when user retry, move them to FAILED_MAILS_SUB_DIR
634
634
  async fn handle_err_while_importing_chunk(
635
635
  &self,
636
636
  import_error: MailImportErrorMessage,
@@ -640,8 +640,8 @@ impl Importer {
640
640
  // if the import is (temporary) disabled, we should give up and let user try again later
641
641
  ImportErrorKind::ImportFeatureDisabled => Err(ImportErrorKind::ImportFeatureDisabled)?,
642
642
 
643
- // If server do not give any available blob storage client ( which we also use to make service call )
644
- // nothing we can do about, should error out in client
643
+ // If a server does not give any available blob storage client (which we also use to make service call),
644
+ // nothing we can do about should error out in a client
645
645
  ImportErrorKind::EmptyBlobServerList => Err(ImportErrorKind::SdkError)?,
646
646
 
647
647
  ImportErrorKind::SdkError => Err(import_error)?,
@@ -739,3 +739,178 @@ impl From<ImportMailStateId> for IdTupleGenerated {
739
739
  }
740
740
  }
741
741
  }
742
+
743
+ #[cfg(test)]
744
+ pub mod tests {
745
+ use super::*;
746
+ use crate::test_utils::{init_file_importer, write_big_sample_email, CleanDir};
747
+
748
+ #[cfg_attr(
749
+ not(feature = "test-with-local-http-server"),
750
+ ignore = "require local http server."
751
+ )]
752
+ #[tokio::test]
753
+ async fn can_import_single_eml_file_without_attachment() {
754
+ let importer = init_file_importer(vec!["sample.eml"]).await;
755
+ importer.start_stateful_import().await.unwrap();
756
+
757
+ let remote_state = importer.essentials.load_remote_state().await.unwrap();
758
+ assert_eq!(remote_state.status, ImportStatus::Finished as i64);
759
+ assert_eq!(remote_state.failedMails, 0);
760
+ assert_eq!(remote_state.successfulMails, 1);
761
+ }
762
+
763
+ #[cfg_attr(
764
+ not(feature = "test-with-local-http-server"),
765
+ ignore = "require local https server."
766
+ )]
767
+ #[tokio::test]
768
+ async fn can_import_single_eml_file_with_attachment() {
769
+ let importer = init_file_importer(vec!["attachment_sample.eml"]).await;
770
+ importer.start_stateful_import().await.unwrap();
771
+
772
+ let remote_state = importer.essentials.load_remote_state().await.unwrap();
773
+ assert_eq!(remote_state.status, ImportStatus::Finished as i64);
774
+ assert_eq!(remote_state.failedMails, 0);
775
+ assert_eq!(remote_state.successfulMails, 1);
776
+ }
777
+
778
+ #[test]
779
+ fn max_request_size_in_test_is_different() {
780
+ assert_eq!(1024 * 5, MAX_REQUEST_SIZE);
781
+ }
782
+
783
+ #[tokio::test]
784
+ async fn existing_import_should_be_none_if_no_state_file() {
785
+ let config_dir_string = "/tmp/existing_import_should_be_none_if_no_state_file";
786
+ let mailbox_id = "some_mailbox_id";
787
+ let import_dir: PathBuf = [
788
+ config_dir_string.to_string(),
789
+ "current_imports".to_string(),
790
+ mailbox_id.to_string(),
791
+ ]
792
+ .iter()
793
+ .collect();
794
+
795
+ let result = Importer::get_existing_import_id(&import_dir);
796
+ assert!(matches!(result, Ok(None)));
797
+ }
798
+
799
+ #[cfg_attr(
800
+ not(feature = "test-with-local-http-server"),
801
+ ignore = "require local http server."
802
+ )]
803
+ #[tokio::test]
804
+ async fn importing_too_big_eml_should_fail() {
805
+ let whither = "/tmp/toobig.eml";
806
+ write_big_sample_email(whither);
807
+
808
+ let importer = init_file_importer(vec![whither]).await;
809
+ let import_res = importer.start_stateful_import().await;
810
+ assert!(importer
811
+ .essentials
812
+ .import_directory
813
+ .join(FAILED_MAILS_SUB_DIR)
814
+ .join("toobig-0.eml")
815
+ .try_exists()
816
+ .unwrap());
817
+
818
+ assert_eq!(
819
+ ImportErrorKind::SourceExhaustedSomeError,
820
+ import_res.unwrap_err().kind
821
+ );
822
+
823
+ let remote_state = importer.essentials.load_remote_state().await.unwrap();
824
+ assert_eq!(remote_state.status, ImportStatus::Finished as i64);
825
+ assert_eq!(remote_state.failedMails, 1);
826
+ assert_eq!(remote_state.successfulMails, 0);
827
+ }
828
+
829
+ #[test]
830
+ fn get_resumable_state_id_invalid_content() {
831
+ let config_dir_string = "/tmp/get_resumable_state_id_invalid_content";
832
+ let mailbox_id = "some_mailbox_id";
833
+ let import_dir: PathBuf = [
834
+ config_dir_string.to_string(),
835
+ "current_imports".to_string(),
836
+ mailbox_id.to_string(),
837
+ ]
838
+ .iter()
839
+ .collect();
840
+ let config_dir = PathBuf::from(config_dir_string);
841
+
842
+ let _tear_down = CleanDir {
843
+ dir: config_dir.clone(),
844
+ };
845
+
846
+ if !import_dir.exists() {
847
+ fs::create_dir_all(&import_dir).unwrap();
848
+ }
849
+ let mut state_id_file_path = import_dir.clone();
850
+ state_id_file_path.push(STATE_ID_FILE_NAME);
851
+ let invalid_id = "blah";
852
+ fs::write(&state_id_file_path, invalid_id).unwrap();
853
+
854
+ let result = Importer::get_existing_import_id(&import_dir)
855
+ .unwrap_err()
856
+ .kind();
857
+ assert_eq!(result, std::io::ErrorKind::InvalidData);
858
+ }
859
+
860
+ #[cfg_attr(
861
+ not(feature = "test-with-local-http-server"),
862
+ ignore = "require local https server."
863
+ )]
864
+ #[tokio::test]
865
+ async fn should_stop_if_on_stop_action() {
866
+ let importer = init_file_importer(vec!["sample.eml"; 1]).await;
867
+ importer
868
+ .set_next_progress_action(ImportProgressAction::Stop)
869
+ .await;
870
+ importer.start_stateful_import().await.unwrap();
871
+
872
+ let mut iterator = importer.chunked_import_source.lock().await;
873
+
874
+ // if we set progress action to stop, it should not have consumed any iterator
875
+ assert!(iterator.next().is_some());
876
+ assert!(iterator.next().is_none());
877
+ }
878
+
879
+ #[cfg_attr(
880
+ not(feature = "test-with-local-http-server"),
881
+ ignore = "require local http server."
882
+ )]
883
+ #[tokio::test]
884
+ async fn should_pause_if_on_pause_action() {
885
+ let importer = init_file_importer(vec!["sample.eml"; 2]).await;
886
+ importer
887
+ .set_next_progress_action(ImportProgressAction::Pause)
888
+ .await;
889
+ importer.start_stateful_import().await.unwrap();
890
+
891
+ let mut iterator = importer.chunked_import_source.lock().await;
892
+
893
+ // if we set progress action to pause, it should not have consumed any iterator
894
+ assert!(iterator.next().is_some());
895
+ assert!(iterator.next().is_some());
896
+ assert!(iterator.next().is_none());
897
+ }
898
+
899
+ #[cfg_attr(
900
+ not(feature = "test-with-local-http-server"),
901
+ ignore = "require local http server."
902
+ )]
903
+ #[tokio::test]
904
+ async fn should_continue_if_on_continue_action() {
905
+ let importer = init_file_importer(vec!["sample.eml"; 1]).await;
906
+ importer
907
+ .set_next_progress_action(ImportProgressAction::Continue)
908
+ .await;
909
+ importer.start_stateful_import().await.unwrap();
910
+
911
+ let mut iterator = importer.chunked_import_source.lock().await;
912
+
913
+ // if we set progress action to continue, it should have consumed all the chunks
914
+ assert!(iterator.next().is_none());
915
+ }
916
+ }