diff --git a/Cargo.lock b/Cargo.lock index 6813700..41f1f68 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2020,6 +2020,7 @@ dependencies = [ "deku", "env_logger", "fsv-sys", + "insta", "libc", "log", "num_enum", @@ -2843,6 +2844,18 @@ dependencies = [ "libc", ] +[[package]] +name = "insta" +version = "1.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6593a41c7a73841868772495db7dc1e8ecab43bb5c0b6da2059246c4b506ab60" +dependencies = [ + "console", + "lazy_static", + "linked-hash-map", + "similar", +] + [[package]] name = "instant" version = "0.1.13" @@ -3137,6 +3150,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + [[package]] name = "linux-raw-sys" version = "0.3.8" @@ -5384,6 +5403,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" +[[package]] +name = "similar" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" + [[package]] name = "siphasher" version = "0.3.11" diff --git a/README.md b/README.md index fe83c6a..d607622 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,20 @@ cargo tauri dev --no-watch > - le `backend` n'est rechargé que si des modifications sont détectées dans le dossier précisé par `-w crates/backend` > - le rechargement du `desktop` est désactivé par l'option `--no-watch` ; en effet, le rechargement du `frontend` est déjà pris en charge par `bun` et ne nécessite pas de rechargement du `desktop` +## Tests + +Les tests unitaires et d'intégration utilisants des cartes CPS ou Vitale réelles sont désactivés par défaut et doivent être +[explicitement activés](https://doc.rust-lang.org/book/ch11-02-running-tests.html#ignoring-some-tests-unless-specifically-requested) pour s'exécuter. + +### Snapshots + +Pour certains modules, des tests d'intégration s'appuient sur la crate [`insta`](https://insta.rs) pour générer et comparer des snapshots. +Pour faciliter la gestion de ces tests, il est recommandé d'installer `cargo-insta` : + +```bash +cargo install cargo-insta +``` + ## Build Pour packager le client `desktop`, il est nécessaire de faire appel à la CLI Tauri, qui se charge de gérer le build du `frontend` et son intégration au bundle : diff --git a/crates/fsv/Cargo.toml b/crates/fsv/Cargo.toml index dad118b..e6fdf3b 100644 --- a/crates/fsv/Cargo.toml +++ b/crates/fsv/Cargo.toml @@ -18,3 +18,11 @@ utils = { path = "../utils" } #[dev-dependencies] log = "0.4.22" env_logger = "0.11.5" + +[dev-dependencies] +insta = "1.40.0" + +[profile.dev.package] +# Optimize insta (snapshot testing library) for faster compile times +insta.opt-level = 3 +similar.opt-level = 3 diff --git a/crates/fsv/src/ssv/mod.rs b/crates/fsv/src/ssv/mod.rs index a8f4385..dbf7fc9 100644 --- a/crates/fsv/src/ssv/mod.rs +++ b/crates/fsv/src/ssv/mod.rs @@ -14,7 +14,7 @@ use fsv_sys::{ mod errors_ssv; -use errors_ssv::SSVErrorCodes; +pub use errors_ssv::SSVErrorCodes; use crate::fsv_parsing::prelude::*; #[derive(Error, Debug)] diff --git a/crates/fsv/tests/snapshots/test_ssv__read_cps_default_kit_doc.snap b/crates/fsv/tests/snapshots/test_ssv__read_cps_default_kit_doc.snap new file mode 100644 index 0000000..9b543c8 --- /dev/null +++ b/crates/fsv/tests/snapshots/test_ssv__read_cps_default_kit_doc.snap @@ -0,0 +1,97 @@ +--- +source: crates/fsv/tests/test_ssv.rs +expression: cps_blocks +--- +Data { + blocks: [ + DataBlock { + header: BlockHeader { + group_id: GroupId( + 1, + ), + data_size: 36, + }, + content: LireCartePS_Group1_Holder( + Holder { + card_type: CPS, + national_id_type: RPPS, + national_id: AlphaNumericString( + "99700619010", + ), + national_id_key: AlphaNumericString( + "0", + ), + civility_code: Monsieur, + holder_lastname: AlphaNumericString( + "DOC0061901", + ), + holder_firstname: AlphaNumericString( + "KIT", + ), + category_card: Unknown, + }, + ), + }, + DataBlock { + header: BlockHeader { + group_id: GroupId( + 2, + ), + data_size: 84, + }, + content: LireCartePS_Group2_Situation( + Situation { + id: RawByte( + 2, + ), + practice_mode: Liberal, + practice_status_raw: NumericString( + "1", + ), + activity_sector: CabinetIndividuel, + structure_id_type: RPPSCabinet, + structure_id: AlphaNumericString( + "99700619010009", + ), + structure_id_key: NumericString( + "0", + ), + structure_name: AlphaNumericString( + "CABINET M DOC0061901", + ), + ps_billing_number: NumericString( + "00102901", + ), + ps_billing_number_key: NumericString( + "6", + ), + ps_replacement_number: AlphaNumericString( + "", + ), + ps_replacement_number_key: NumericString( + "0", + ), + convention_code: Conventionne, + specialty_code: MedecineGenerale, + rate_zone_code_raw: NumericString( + "24", + ), + ik_zone_code: IndemniteKilometriquePlaine, + approval_code_1: PasAgrementRadio, + approval_code_2_raw: NumericString( + "0", + ), + approval_code_3_raw: NumericString( + "0", + ), + invoice_signature_permission: NumericString( + "1", + ), + lot_signature_permission: NumericString( + "1", + ), + }, + ), + }, + ], +} diff --git a/crates/fsv/tests/snapshots/test_ssv__read_cps_pharma_kit_adjointe.snap b/crates/fsv/tests/snapshots/test_ssv__read_cps_pharma_kit_adjointe.snap new file mode 100644 index 0000000..c64f770 --- /dev/null +++ b/crates/fsv/tests/snapshots/test_ssv__read_cps_pharma_kit_adjointe.snap @@ -0,0 +1,97 @@ +--- +source: crates/fsv/tests/test_ssv.rs +expression: cps_blocks +--- +Data { + blocks: [ + DataBlock { + header: BlockHeader { + group_id: GroupId( + 1, + ), + data_size: 55, + }, + content: LireCartePS_Group1_Holder( + Holder { + card_type: CPS, + national_id_type: RPPS, + national_id: AlphaNumericString( + "99700593694", + ), + national_id_key: AlphaNumericString( + "4", + ), + civility_code: Madame, + holder_lastname: AlphaNumericString( + "ADJOINTPHARM RPPS0059369", + ), + holder_firstname: AlphaNumericString( + "PATRICIA", + ), + category_card: Unknown, + }, + ), + }, + DataBlock { + header: BlockHeader { + group_id: GroupId( + 2, + ), + data_size: 93, + }, + content: LireCartePS_Group2_Situation( + Situation { + id: RawByte( + 1, + ), + practice_mode: Salarie, + practice_status_raw: NumericString( + "2", + ), + activity_sector: PharmacieDOfficine, + structure_id_type: FINESS, + structure_id: AlphaNumericString( + "0B0246286", + ), + structure_id_key: NumericString( + "6", + ), + structure_name: AlphaNumericString( + "PHARMACIE DE LA GARE ROUTIERE24628", + ), + ps_billing_number: NumericString( + "00209368", + ), + ps_billing_number_key: NumericString( + "0", + ), + ps_replacement_number: AlphaNumericString( + "", + ), + ps_replacement_number_key: NumericString( + "0", + ), + convention_code: Conventionne, + specialty_code: PharmacieDOfficine, + rate_zone_code_raw: NumericString( + "10", + ), + ik_zone_code: PasDIndemniteKilometrique, + approval_code_1: PasAgrementRadio, + approval_code_2_raw: NumericString( + "0", + ), + approval_code_3_raw: NumericString( + "0", + ), + invoice_signature_permission: NumericString( + "1", + ), + lot_signature_permission: NumericString( + "1", + ), + }, + ), + }, + ], +} diff --git a/crates/fsv/tests/snapshots/test_ssv__read_cps_pharma_kit_employe_aa.snap b/crates/fsv/tests/snapshots/test_ssv__read_cps_pharma_kit_employe_aa.snap new file mode 100644 index 0000000..a580c03 --- /dev/null +++ b/crates/fsv/tests/snapshots/test_ssv__read_cps_pharma_kit_employe_aa.snap @@ -0,0 +1,97 @@ +--- +source: crates/fsv/tests/test_ssv.rs +expression: cps_blocks +--- +Data { + blocks: [ + DataBlock { + header: BlockHeader { + group_id: GroupId( + 1, + ), + data_size: 49, + }, + content: LireCartePS_Group1_Holder( + Holder { + card_type: CPE, + national_id_type: FINESS, + national_id: AlphaNumericString( + "0B0246286/CPET00001", + ), + national_id_key: AlphaNumericString( + "6", + ), + civility_code: Monsieur, + holder_lastname: AlphaNumericString( + "EMPLOYE246280001", + ), + holder_firstname: AlphaNumericString( + "AA", + ), + category_card: Unknown, + }, + ), + }, + DataBlock { + header: BlockHeader { + group_id: GroupId( + 2, + ), + data_size: 93, + }, + content: LireCartePS_Group2_Situation( + Situation { + id: RawByte( + 1, + ), + practice_mode: Salarie, + practice_status_raw: NumericString( + "0", + ), + activity_sector: PharmacieDOfficine, + structure_id_type: FINESS, + structure_id: AlphaNumericString( + "0B0246286", + ), + structure_id_key: NumericString( + "6", + ), + structure_name: AlphaNumericString( + "PHARMACIE DE LA GARE ROUTIERE24628", + ), + ps_billing_number: NumericString( + "00209368", + ), + ps_billing_number_key: NumericString( + "0", + ), + ps_replacement_number: AlphaNumericString( + "", + ), + ps_replacement_number_key: NumericString( + "0", + ), + convention_code: Conventionne, + specialty_code: PharmacieDOfficine, + rate_zone_code_raw: NumericString( + "10", + ), + ik_zone_code: PasDIndemniteKilometrique, + approval_code_1: PasAgrementRadio, + approval_code_2_raw: NumericString( + "0", + ), + approval_code_3_raw: NumericString( + "0", + ), + invoice_signature_permission: NumericString( + "1", + ), + lot_signature_permission: NumericString( + "0", + ), + }, + ), + }, + ], +} diff --git a/crates/fsv/tests/snapshots/test_ssv__read_cps_pharma_kit_infirmiere.snap b/crates/fsv/tests/snapshots/test_ssv__read_cps_pharma_kit_infirmiere.snap new file mode 100644 index 0000000..4dcce64 --- /dev/null +++ b/crates/fsv/tests/snapshots/test_ssv__read_cps_pharma_kit_infirmiere.snap @@ -0,0 +1,97 @@ +--- +source: crates/fsv/tests/test_ssv.rs +expression: cps_blocks +--- +Data { + blocks: [ + DataBlock { + header: BlockHeader { + group_id: GroupId( + 1, + ), + data_size: 50, + }, + content: LireCartePS_Group1_Holder( + Holder { + card_type: CPS, + national_id_type: RPPS, + national_id: AlphaNumericString( + "99700520499", + ), + national_id_key: AlphaNumericString( + "9", + ), + civility_code: Madame, + holder_lastname: AlphaNumericString( + "INFIRMIERE RPPS0052049", + ), + holder_firstname: AlphaNumericString( + "ALINE", + ), + category_card: Unknown, + }, + ), + }, + DataBlock { + header: BlockHeader { + group_id: GroupId( + 2, + ), + data_size: 93, + }, + content: LireCartePS_Group2_Situation( + Situation { + id: RawByte( + 1, + ), + practice_mode: Liberal, + practice_status_raw: NumericString( + "1", + ), + activity_sector: CabinetIndividuel, + structure_id_type: RPPSCabinet, + structure_id: AlphaNumericString( + "99700520499002", + ), + structure_id_key: NumericString( + "0", + ), + structure_name: AlphaNumericString( + "CABINET MME INFIRMIERE0052049", + ), + ps_billing_number: NumericString( + "00602049", + ), + ps_billing_number_key: NumericString( + "9", + ), + ps_replacement_number: AlphaNumericString( + "", + ), + ps_replacement_number_key: NumericString( + "0", + ), + convention_code: Conventionne, + specialty_code: Infirmier, + rate_zone_code_raw: NumericString( + "20", + ), + ik_zone_code: IndemniteKilometriqueMontagne, + approval_code_1: PasAgrementRadio, + approval_code_2_raw: NumericString( + "0", + ), + approval_code_3_raw: NumericString( + "0", + ), + invoice_signature_permission: NumericString( + "1", + ), + lot_signature_permission: NumericString( + "1", + ), + }, + ), + }, + ], +} diff --git a/crates/fsv/tests/snapshots/test_ssv__read_cps_pharma_kit_titulaire.snap b/crates/fsv/tests/snapshots/test_ssv__read_cps_pharma_kit_titulaire.snap new file mode 100644 index 0000000..eda78c8 --- /dev/null +++ b/crates/fsv/tests/snapshots/test_ssv__read_cps_pharma_kit_titulaire.snap @@ -0,0 +1,97 @@ +--- +source: crates/fsv/tests/test_ssv.rs +expression: cps_blocks +--- +Data { + blocks: [ + DataBlock { + header: BlockHeader { + group_id: GroupId( + 1, + ), + data_size: 53, + }, + content: LireCartePS_Group1_Holder( + Holder { + card_type: CPS, + national_id_type: RPPS, + national_id: AlphaNumericString( + "99700593686", + ), + national_id_key: AlphaNumericString( + "6", + ), + civility_code: Monsieur, + holder_lastname: AlphaNumericString( + "PHARMOFFICE RPPS0059368", + ), + holder_firstname: AlphaNumericString( + "GILBERT", + ), + category_card: Unknown, + }, + ), + }, + DataBlock { + header: BlockHeader { + group_id: GroupId( + 2, + ), + data_size: 93, + }, + content: LireCartePS_Group2_Situation( + Situation { + id: RawByte( + 1, + ), + practice_mode: Liberal, + practice_status_raw: NumericString( + "1", + ), + activity_sector: PharmacieDOfficine, + structure_id_type: FINESS, + structure_id: AlphaNumericString( + "0B0246286", + ), + structure_id_key: NumericString( + "6", + ), + structure_name: AlphaNumericString( + "PHARMACIE DE LA GARE ROUTIERE24628", + ), + ps_billing_number: NumericString( + "00209368", + ), + ps_billing_number_key: NumericString( + "0", + ), + ps_replacement_number: AlphaNumericString( + "", + ), + ps_replacement_number_key: NumericString( + "0", + ), + convention_code: Conventionne, + specialty_code: PharmacieDOfficine, + rate_zone_code_raw: NumericString( + "10", + ), + ik_zone_code: PasDIndemniteKilometrique, + approval_code_1: PasAgrementRadio, + approval_code_2_raw: NumericString( + "0", + ), + approval_code_3_raw: NumericString( + "0", + ), + invoice_signature_permission: NumericString( + "1", + ), + lot_signature_permission: NumericString( + "1", + ), + }, + ), + }, + ], +} diff --git a/crates/fsv/tests/test_ssv.rs b/crates/fsv/tests/test_ssv.rs new file mode 100644 index 0000000..8fc921b --- /dev/null +++ b/crates/fsv/tests/test_ssv.rs @@ -0,0 +1,174 @@ +use anyhow::Result; + +use common::read_cps; +use fsv::ssv::{Error, SSVErrorCodes, SSV}; +use insta::assert_debug_snapshot; + +mod common { + + use super::*; + + use std::env; + + use fsv::fsv_parsing::Data; + use fsv_sys::SupportedFsvVersion; + use utils::config::load_config; + + pub fn init() -> Result { + load_config(None)?; + let sesam_ini_path = env::var("SESAM_INI_PATH").expect("SESAM_INI_PATH must be set"); + let lib = SSV::new(SupportedFsvVersion::V1_40_13)?; + lib.init_library(&sesam_ini_path)?; + Ok(lib) + } + + pub fn read_cps() -> Result { + let lib = init()?; + Ok(lib.read_professional_card("1234")?) + } +} + +// ---------------- PHARMA KIT ---------------- + +#[test] +#[ignore] +fn test_read_cps_pharma_kit_titulaire() -> Result<()> { + let cps_blocks = read_cps()?; + assert_debug_snapshot!(cps_blocks); + Ok(()) +} + +#[test] +#[ignore] +fn test_read_cps_pharma_kit_adjointe() -> Result<()> { + let cps_blocks = read_cps()?; + assert_debug_snapshot!(cps_blocks); + Ok(()) +} + +#[test] +#[ignore] +fn test_read_cps_pharma_kit_employe_aa() -> Result<()> { + let cps_blocks = read_cps()?; + assert_debug_snapshot!(cps_blocks); + Ok(()) +} + +#[test] +#[ignore] +fn test_read_cps_pharma_kit_infirmiere() -> Result<()> { + let cps_blocks = read_cps()?; + assert_debug_snapshot!(cps_blocks); + Ok(()) +} + +// ---------------- DEFAULT KIT ---------------- + +#[test] +#[ignore] +fn test_read_cps_default_kit_doc() -> Result<()> { + let cps_blocks = read_cps()?; + assert_debug_snapshot!(cps_blocks); + Ok(()) +} + +#[test] +#[ignore] +fn test_read_cps_default_kit_directeur() -> Result<()> { + let error = read_cps().expect_err("This card should be opposed and not be readable"); + match error.downcast_ref::() { + Some(Error::SSVError(e)) => { + match e { + SSVErrorCodes::CPSInvalid => { Ok(()) }, + _ => panic!("Expected SSVErrorCodes::CPSInvalid, got {:?}", e), + } + }, + _ => panic!("Expected Error::SSVError got {:?}", error), + } +} + +#[test] +#[ignore] +fn test_read_cps_default_kit_employee_opposee() -> Result<()> { + let error = read_cps().expect_err("This card should be opposed and not be readable"); + match error.downcast_ref::() { + Some(Error::SSVError(e)) => { + match e { + SSVErrorCodes::SSVInternalError => { Ok(()) }, + _ => panic!("Expected SSVErrorCodes::SSVInternalError, got {:?}", e), + } + }, + _ => panic!("Expected Error::SSVError got {:?}", error), + } +} + +#[test] +#[ignore] +fn test_read_cps_default_kit_doc_maximaxima() -> Result<()> { + // TODO : debug this card (ConventionCode value is 9) + let cps_blocks = read_cps()?; + assert_debug_snapshot!(cps_blocks); + Ok(()) +} + +#[test] +#[ignore] +fn test_read_cps_default_kit_orthophoniste() -> Result<()> { + let error = read_cps().expect_err("This card should be opposed and not be readable"); + match error.downcast_ref::() { + Some(Error::SSVError(e)) => { + match e { + SSVErrorCodes::SSVInternalError => { Ok(()) }, + _ => panic!("Expected SSVErrorCodes::SSVInternalError, got {:?}", e), + } + }, + _ => panic!("Expected Error::SSVError got {:?}", error), + } +} + +// ---------------- CENTRE SANTÉ ---------------- + +#[test] +#[ignore] +fn test_read_cps_centre_sante_kit_directeur() -> Result<()> { + // TODO : debug this card (ConventionCode value is 9) + let cps_blocks = read_cps()?; + assert_debug_snapshot!(cps_blocks); + Ok(()) +} + +#[test] +#[ignore] +fn test_read_cps_centre_sante_kit_doc() -> Result<()> { + // TODO : debug this card (ConventionCode value is 9) + let cps_blocks = read_cps()?; + assert_debug_snapshot!(cps_blocks); + Ok(()) +} + +#[test] +#[ignore] +fn test_read_cps_centre_sante_kit_dentiste() -> Result<()> { + // TODO : debug this card (ConventionCode value is 9) + let cps_blocks = read_cps()?; + assert_debug_snapshot!(cps_blocks); + Ok(()) +} + +#[test] +#[ignore] +fn test_read_cps_centre_sante_kit_assist_vladimir() -> Result<()> { + // TODO : debug this card (ConventionCode value is 9) + let cps_blocks = read_cps()?; + assert_debug_snapshot!(cps_blocks); + Ok(()) +} + +#[test] +#[ignore] +fn test_read_cps_centre_sante_kit_assist_marie() -> Result<()> { + // TODO : debug this card (ConventionCode value is 9) + let cps_blocks = read_cps()?; + assert_debug_snapshot!(cps_blocks); + Ok(()) +} \ No newline at end of file