From 594f33d4a5d69af47d99b3c6df75d1d9f3675f5d Mon Sep 17 00:00:00 2001
From: Egor Ivkov <e.o.ivkov@gmail.com>
Date: Thu, 4 May 2023 19:31:25 +0300
Subject: [PATCH] extensive dml tests

---
 src/traft/rpc/cas.rs | 113 +++++++++++++++++++++++++++++++++----------
 test/int/test_cas.py |  29 +++++------
 2 files changed, 103 insertions(+), 39 deletions(-)

diff --git a/src/traft/rpc/cas.rs b/src/traft/rpc/cas.rs
index d969e3f338..b32e538b1f 100644
--- a/src/traft/rpc/cas.rs
+++ b/src/traft/rpc/cas.rs
@@ -1,5 +1,5 @@
+use crate::storage::TClusterwideSpace as _;
 use crate::storage::{ClusterwideSpace, Indexes, Properties, Spaces};
-use crate::storage::{PropertyName, TClusterwideSpace as _};
 use crate::tlog;
 use crate::traft::error::Error as TraftError;
 use crate::traft::node;
@@ -346,7 +346,7 @@ fn modifies_operable(op: &Op, space: &str, properties: &Properties) -> bool {
         Op::DdlPrepare { ddl, .. } => ddl_modifies(ddl),
         Op::DdlCommit | Op::DdlAbort => {
             if let Some(change) = properties
-                .get::<Ddl>(PropertyName::PendingSchemaChange)
+                .pending_schema_change()
                 .expect("conversion should not fail")
             {
                 ddl_modifies(&change)
@@ -359,6 +359,7 @@ fn modifies_operable(op: &Op, space: &str, properties: &Properties) -> bool {
 }
 
 mod tests {
+    use serde::Serialize;
     use tarantool::tuple::ToTupleBuffer;
 
     use crate::storage::TClusterwideSpace as _;
@@ -367,7 +368,7 @@ mod tests {
     use super::*;
 
     #[::tarantool::test]
-    fn check_ddl_predicate() {
+    fn ddl() {
         let clusterwide = Clusterwide::new().unwrap();
 
         let predicate = Predicate {
@@ -405,27 +406,46 @@ mod tests {
     }
 
     #[::tarantool::test]
-    fn check_bounds() {
+    fn dml() {
+        #[derive(Debug)]
+        enum TestOp {
+            Insert,
+            Replace,
+            Update,
+            Delete,
+        }
+
         #[track_caller]
-        fn test(range: Range, test_cases: &[&str], storage: &Clusterwide) -> Vec<bool> {
+        fn test<T: Serialize>(
+            op: &TestOp,
+            range: &Range,
+            test_cases: &[T],
+            storage: &Clusterwide,
+        ) -> Vec<bool> {
             let predicate = Predicate {
                 index: 1,
                 term: 1,
-                ranges: vec![range],
+                ranges: vec![range.clone()],
             };
             test_cases
                 .iter()
-                .map(|&case| (String::from(case),).to_tuple_buffer().unwrap())
+                .map(|case| (case,).to_tuple_buffer().unwrap())
                 .map(|key| {
+                    let space = ClusterwideSpace::Property;
+                    match op {
+                        TestOp::Insert => Dml::Insert { space, tuple: key },
+                        TestOp::Replace => Dml::Replace { space, tuple: key },
+                        TestOp::Update => Dml::Update {
+                            space,
+                            key,
+                            ops: vec![],
+                        },
+                        TestOp::Delete => Dml::Delete { space, key },
+                    }
+                })
+                .map(|op| {
                     predicate
-                        .check_entry(
-                            2,
-                            &Op::Dml(Dml::Delete {
-                                space: ClusterwideSpace::Property,
-                                key,
-                            }),
-                            &storage.properties,
-                        )
+                        .check_entry(2, &Op::Dml(op), &storage.properties)
                         .is_err()
                 })
                 .collect()
@@ -434,6 +454,12 @@ mod tests {
         let storage = Clusterwide::new().unwrap();
 
         let test_cases = ["a", "b", "c", "d", "e"];
+        let ops = &[
+            TestOp::Insert,
+            TestOp::Replace,
+            TestOp::Update,
+            TestOp::Delete,
+        ];
 
         // For range ["b", "d"]
         // Test cases a,b,c,d,e
@@ -443,8 +469,10 @@ mod tests {
             key_min: Bound::Included(("b",).to_tuple_buffer().unwrap()),
             key_max: Bound::Included(("d",).to_tuple_buffer().unwrap()),
         };
-        let errors = test(range, &test_cases, &storage);
-        assert_eq!(errors, vec![false, true, true, true, false]);
+        for op in ops {
+            let errors = test(&op, &range, &test_cases, &storage);
+            assert_eq!(errors, vec![false, true, true, true, false], "{op:?}");
+        }
 
         // For range ("b", "d"]
         // Test cases a,b,c,d,e
@@ -454,8 +482,10 @@ mod tests {
             key_min: Bound::Excluded(("b",).to_tuple_buffer().unwrap()),
             key_max: Bound::Included(("d",).to_tuple_buffer().unwrap()),
         };
-        let errors = test(range, &test_cases, &storage);
-        assert_eq!(errors, vec![false, false, true, true, false]);
+        for op in ops {
+            let errors = test(&op, &range, &test_cases, &storage);
+            assert_eq!(errors, vec![false, false, true, true, false], "{op:?}");
+        }
 
         // For range ["b", "d")
         // Test cases a,b,c,d,e
@@ -465,8 +495,10 @@ mod tests {
             key_min: Bound::Included(("b",).to_tuple_buffer().unwrap()),
             key_max: Bound::Excluded(("d",).to_tuple_buffer().unwrap()),
         };
-        let errors = test(range, &test_cases, &storage);
-        assert_eq!(errors, vec![false, true, true, false, false]);
+        for op in ops {
+            let errors = test(&op, &range, &test_cases, &storage);
+            assert_eq!(errors, vec![false, true, true, false, false], "{op:?}");
+        }
 
         // For range _, "d"]
         // Test cases a,b,c,d,e
@@ -476,8 +508,10 @@ mod tests {
             key_min: Bound::Unbounded,
             key_max: Bound::Included(("d",).to_tuple_buffer().unwrap()),
         };
-        let errors = test(range, &test_cases, &storage);
-        assert_eq!(errors, vec![true, true, true, true, false]);
+        for op in ops {
+            let errors = test(&op, &range, &test_cases, &storage);
+            assert_eq!(errors, vec![true, true, true, true, false], "{op:?}");
+        }
 
         // For range ["b", _
         // Test cases a,b,c,d,e
@@ -487,7 +521,36 @@ mod tests {
             key_min: Bound::Included(("b",).to_tuple_buffer().unwrap()),
             key_max: Bound::Unbounded,
         };
-        let errors = test(range, &test_cases, &storage);
-        assert_eq!(errors, vec![false, true, true, true, true]);
+        for op in ops {
+            let errors = test(&op, &range, &test_cases, &storage);
+            assert_eq!(errors, vec![false, true, true, true, true], "{op:?}");
+        }
+
+        // For range _, _
+        // Test cases a,b,c,d,e
+        // Error      1,1,1,1,1
+        let range = Range {
+            space: Properties::SPACE_NAME.into(),
+            key_min: Bound::Unbounded,
+            key_max: Bound::Unbounded,
+        };
+        for op in ops {
+            let errors = test(&op, &range, &test_cases, &storage);
+            assert_eq!(errors, vec![true, true, true, true, true], "{op:?}");
+        }
+
+        // Different space
+        // For range _, _
+        // Test cases a
+        // Error      0
+        let range = Range {
+            space: Spaces::SPACE_NAME.into(),
+            key_min: Bound::Unbounded,
+            key_max: Bound::Unbounded,
+        };
+        for op in ops {
+            let errors = test(&op, &range, &[1], &storage);
+            assert_eq!(errors, vec![false], "{op:?}");
+        }
     }
 }
diff --git a/test/int/test_cas.py b/test/int/test_cas.py
index d73704bc09..4035042b22 100644
--- a/test/int/test_cas.py
+++ b/test/int/test_cas.py
@@ -82,21 +82,22 @@ def test_cas_errors(instance: Instance):
     )
 
     # Prohibited spaces
-    with pytest.raises(TarantoolError) as e5:
-        instance.cas(
-            "insert",
-            "_picodata_space",
-            [0],
-            range=(
-                dict(kind="included", value=0),
-                dict(kind="included", value=0),
-            ),
+    for space in ["_picodata_space", "_picodata_index"]:
+        with pytest.raises(TarantoolError) as e5:
+            instance.cas(
+                "insert",
+                space,
+                [0],
+                range=(
+                    dict(kind="included", value=0),
+                    dict(kind="included", value=0),
+                ),
+            )
+        assert e5.value.args == (
+            "ER_PROC_C",
+            f"compare-and-swap request failed: space {space} is prohibited for use "
+            + "in a predicate",
         )
-    assert e5.value.args == (
-        "ER_PROC_C",
-        "compare-and-swap request failed: space _picodata_space is prohibited for use "
-        + "in a predicate",
-    )
 
 
 def test_cas_predicate(instance: Instance):
-- 
GitLab