Repository: flipt-io/flipt-client-sdks (language: python)

Issue:
fix(wasm): prevent panic in the wasm
Fixes: #377


Discussion / hints:

Fix patch:
diff --git a/flipt-engine-wasm/README.md b/flipt-engine-wasm/README.md
index b4bdf18f..f540d3b7 100644
--- a/flipt-engine-wasm/README.md
+++ b/flipt-engine-wasm/README.md
@@ -19,3 +19,9 @@ See [ARCHITECTURE.md](./ARCHITECTURE.md).
 ```bash
 wasm-pack build --target [bundler|nodejs|web]
 ```
+
+### Test
+
+```bash
+wasm-pack test --node
+```
diff --git a/flipt-engine-wasm/src/lib.rs b/flipt-engine-wasm/src/lib.rs
index bb34477d..deef3300 100644
--- a/flipt-engine-wasm/src/lib.rs
+++ b/flipt-engine-wasm/src/lib.rs
@@ -17,7 +17,7 @@ where
     error_message: Option<String>,
 }
 
-#[derive(Serialize, Deserialize)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
 enum Status {
     #[serde(rename = "success")]
     Success,
@@ -125,3 +125,71 @@ impl Engine {
         Ok(serde_wasm_bindgen::to_value(&response)?)
     }
 }
+
+#[cfg(test)]
+mod tests {
+
+    use std::collections::HashMap;
+
+    use super::*;
+    use fliptevaluation::EvaluationRequest;
+    use wasm_bindgen_test::wasm_bindgen_test;
+
+    // wasm_bindgen_test_configure!(run_in_browser);
+
+    #[wasm_bindgen_test]
+    fn test_snapshot_with_valid_data() {
+        let mut engine = Engine::new("default");
+        let state = r#"
+        {
+         "namespace": { "key": "default"},
+         "flags": [
+             {
+             "key": "flag1",
+             "name": "flag1",
+             "description": "",
+             "enabled": false,
+             "type": "VARIANT_FLAG_TYPE",
+             "createdAt": "2024-09-13T19:37:18.723909Z",
+             "updatedAt": "2024-09-13T19:37:18.723909Z",
+             "rules": [],
+             "rollouts": []
+        }]}"#;
+
+        let document: source::Document = serde_json::from_str(state).expect("valid snapshot");
+        let data = serde_wasm_bindgen::to_value(&document).unwrap();
+        let result = engine.snapshot(data);
+        assert!(result.is_ok());
+        let flags = engine.list_flags();
+        assert!(flags.is_ok());
+
+        let eval_req = EvaluationRequest {
+            flag_key: "flag1".to_owned(),
+            entity_id: "one".to_owned(),
+            context: HashMap::new(),
+        };
+        let req = serde_wasm_bindgen::to_value(&eval_req).unwrap();
+        let js_value = engine.evaluate_variant(req.clone());
+        assert!(js_value.is_ok());
+
+        let result = engine.evaluate_boolean(req.clone());
+        assert!(result.is_ok());
+
+        let result = engine.evaluate_batch(req);
+        assert!(result.is_ok());
+    }
+
+    #[wasm_bindgen_test]
+    fn test_snapshot_with_invalid_data() {
+        let mut engine = Engine::new("default");
+        let result = engine.snapshot(JsValue::from_str(""));
+        assert!(result.is_err());
+        match result {
+            Ok(_) => panic!("Expected an error, but got Ok"),
+            Err(e) => assert_eq!(
+                e.as_string().unwrap(),
+                "Error: invalid type: string \"\", expected struct Document"
+            ),
+        }
+    }
+}
diff --git a/flipt-evaluation/src/lib.rs b/flipt-evaluation/src/lib.rs
index 2a53e4d8..8a3a28e1 100644
--- a/flipt-evaluation/src/lib.rs
+++ b/flipt-evaluation/src/lib.rs
@@ -17,7 +17,7 @@ const DEFAULT_TOTAL_BUCKET_NUMBER: u32 = 1000;
 const DEFAULT_PERCENT_MULTIPIER: f32 = DEFAULT_TOTAL_BUCKET_NUMBER as f32 / DEFAULT_PERCENT;
 
 #[repr(C)]
-#[derive(Deserialize, Clone, PartialEq, Debug)]
+#[derive(Deserialize, Clone, PartialEq, Debug, Serialize)]
 pub struct EvaluationRequest {
     pub flag_key: String,
     pub entity_id: String,
diff --git a/flipt-evaluation/src/models/source.rs b/flipt-evaluation/src/models/source.rs
index 84951c6b..db481a0f 100644
--- a/flipt-evaluation/src/models/source.rs
+++ b/flipt-evaluation/src/models/source.rs
@@ -1,8 +1,8 @@
-use serde::Deserialize;
+use serde::{Deserialize, Serialize};
 
 use super::flipt;
 
-#[derive(Deserialize)]
+#[derive(Deserialize, Serialize)]
 #[serde(rename_all = "camelCase")]
 pub struct Document {
     pub namespace: Namespace,
@@ -21,14 +21,14 @@ impl Default for Document {
     }
 }
 
-#[derive(Deserialize)]
+#[derive(Deserialize, Serialize)]
 #[serde(rename_all = "camelCase")]
 pub struct Namespace {
     pub key: String,
     pub name: Option<String>,
 }
 
-#[derive(Deserialize)]
+#[derive(Deserialize, Serialize)]
 #[serde(rename_all = "camelCase")]
 pub struct Flag {
     pub key: String,
@@ -41,7 +41,7 @@ pub struct Flag {
     pub default_variant: Option<Variant>,
 }
 
-#[derive(Deserialize)]
+#[derive(Deserialize, Serialize)]
 #[serde(rename_all = "camelCase")]
 pub struct Variant {
     pub id: String,
@@ -49,7 +49,7 @@ pub struct Variant {
     pub attachment: String,
 }
 
-#[derive(Deserialize)]
+#[derive(Deserialize, Serialize)]
 #[serde(rename_all = "camelCase")]
 pub struct Rule {
     pub distributions: Vec<Distribution>,
@@ -57,7 +57,7 @@ pub struct Rule {
     pub segment_operator: flipt::SegmentOperator,
 }
 
-#[derive(Deserialize)]
+#[derive(Deserialize, Serialize)]
 #[serde(rename_all = "camelCase")]
 pub struct Distribution {
     pub variant_key: String,
@@ -65,7 +65,7 @@ pub struct Distribution {
     pub variant_attachment: String,
 }
 
-#[derive(Deserialize)]
+#[derive(Deserialize, Serialize)]
 #[serde(rename_all = "camelCase")]
 pub struct Rollout {
     pub description: Option<String>,
@@ -73,7 +73,7 @@ pub struct Rollout {
     pub threshold: Option<Threshold>,
 }
 
-#[derive(Deserialize)]
+#[derive(Deserialize, Serialize)]
 #[serde(rename_all = "camelCase")]
 pub struct SegmentRule {
     pub segment_operator: Option<flipt::SegmentOperator>,
@@ -81,14 +81,14 @@ pub struct SegmentRule {
     pub segments: Vec<Segment>,
 }
 
-#[derive(Deserialize)]
+#[derive(Deserialize, Serialize)]
 #[serde(rename_all = "camelCase")]
 pub struct Threshold {
     pub percentage: f32,
     pub value: bool,
 }
 
-#[derive(Deserialize)]
+#[derive(Deserialize, Serialize)]
 #[serde(rename_all = "camelCase")]
 pub struct Segment {
     pub key: String,
@@ -96,7 +96,7 @@ pub struct Segment {
     pub constraints: Vec<SegmentConstraint>,
 }
 
-#[derive(Deserialize)]
+#[derive(Deserialize, Serialize)]
 #[serde(rename_all = "camelCase")]
 pub struct SegmentConstraint {
     pub r#type: flipt::ConstraintComparisonType,


Test patch:
diff --git a/.github/workflows/test-engines.yml b/.github/workflows/test-engines.yml
index 25be3a59..794f4405 100644
--- a/.github/workflows/test-engines.yml
+++ b/.github/workflows/test-engines.yml
@@ -19,11 +19,15 @@ jobs:
           profile: minimal
           toolchain: 1.75.0
           override: true
-
       - name: Test
         uses: actions-rs/cargo@v1
         with:
           command: test
+      - name: Install wasm-pack
+        uses: jetli/wasm-pack-action@v0.4.0
+      - name: Test wasm
+        run: wasm-pack test --node
+        working-directory: flipt-engine-wasm
 
   lint:
     name: Lint