This is an automated email from the ASF dual-hosted git repository.

tkalkirill pushed a commit to branch ignite-26649
in repository https://gitbox.apache.org/repos/asf/ignite-3.git

commit 535706b030ddaa0da17274b62058f0f3ddf08ea8
Author: Kirill Tkalenko <[email protected]>
AuthorDate: Thu Oct 9 13:16:24 2025 +0300

    IGNITE-26649 wip
---
 modules/metastorage/build.gradle                   |   1 +
 .../MetastorageCommandsCompatibilityTest.java      | 287 +++++++++++++++++++++
 2 files changed, 288 insertions(+)

diff --git a/modules/metastorage/build.gradle b/modules/metastorage/build.gradle
index 17ba03e0618..e599d32d928 100644
--- a/modules/metastorage/build.gradle
+++ b/modules/metastorage/build.gradle
@@ -44,6 +44,7 @@ dependencies {
 
     annotationProcessor project(':ignite-network-annotation-processor')
 
+    testImplementation project(':ignite-network')
     testImplementation testFixtures(project(':ignite-core'))
     testImplementation testFixtures(project(':ignite-vault'))
     testImplementation testFixtures(project(':ignite-configuration'))
diff --git 
a/modules/metastorage/src/test/java/org/apache/ignite/internal/metastorage/command/MetastorageCommandsCompatibilityTest.java
 
b/modules/metastorage/src/test/java/org/apache/ignite/internal/metastorage/command/MetastorageCommandsCompatibilityTest.java
new file mode 100644
index 00000000000..a2c3f4959a2
--- /dev/null
+++ 
b/modules/metastorage/src/test/java/org/apache/ignite/internal/metastorage/command/MetastorageCommandsCompatibilityTest.java
@@ -0,0 +1,287 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.metastorage.command;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.nio.ByteBuffer;
+import java.util.Base64;
+import java.util.List;
+import java.util.UUID;
+import org.apache.ignite.internal.hlc.HybridTimestamp;
+import org.apache.ignite.internal.lang.ByteArray;
+import org.apache.ignite.internal.metastorage.CommandId;
+import org.apache.ignite.internal.metastorage.dsl.Condition;
+import org.apache.ignite.internal.metastorage.dsl.Conditions;
+import org.apache.ignite.internal.metastorage.dsl.Iif;
+import 
org.apache.ignite.internal.metastorage.dsl.MetaStorageMessagesSerializationRegistryInitializer;
+import org.apache.ignite.internal.metastorage.dsl.Operations;
+import org.apache.ignite.internal.metastorage.dsl.Statements;
+import org.apache.ignite.internal.network.MessageSerializationRegistryImpl;
+import 
org.apache.ignite.internal.network.serialization.MessageSerializationRegistry;
+import org.apache.ignite.internal.raft.Command;
+import org.apache.ignite.internal.raft.Marshaller;
+import org.apache.ignite.internal.raft.util.ThreadLocalOptimizedMarshaller;
+import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Compatibility testing for serialization/deserialization of metastorage raft 
commands. It is verified that deserialization of commands
+ * that were created on earlier versions of the product will be error-free.
+ */
+public class MetastorageCommandsCompatibilityTest extends 
BaseIgniteAbstractTest {
+    private static final MessageSerializationRegistry REGISTRY = new 
MessageSerializationRegistryImpl();
+
+    private static final Marshaller MARSHALLER = new 
ThreadLocalOptimizedMarshaller(REGISTRY);
+
+    @BeforeAll
+    static void beforeAll() {
+        new 
MetaStorageCommandsSerializationRegistryInitializer().registerFactories(REGISTRY);
+        new 
MetaStorageMessagesSerializationRegistryInitializer().registerFactories(REGISTRY);
+    }
+
+    @Test
+    void testCompactionCommand() {
+        CompactionCommand command = decodeCommand("cEkrR0Y=");
+
+        assertEquals(safeTime(), command.safeTime());
+        assertEquals(initiatorTime(), command.initiatorTime());
+        assertEquals(42, command.compactionRevision());
+    }
+
+    @Test
+    void testEvictIdempotentCommandsCacheCommand() {
+        EvictIdempotentCommandsCacheCommand command = 
decodeCommand("cEhlR0Y=");
+
+        assertEquals(safeTime(), command.safeTime());
+        assertEquals(initiatorTime(), command.initiatorTime());
+        assertEquals(HybridTimestamp.hybridTimestamp(100), 
command.evictionTimestamp());
+    }
+
+    @Test
+    void testGetAllCommand() {
+        GetAllCommand command = decodeCommand("cB8DAwVrZXkxAwVrZXkyKw==");
+
+        assertEquals(42, command.revision());
+        assertEquals(List.of(key("key1"), key("key2")), command.keys());
+    }
+
+    @Test
+    void testGetChecksumCommand() {
+        GetChecksumCommand command = decodeCommand("cCMr");
+
+        assertEquals(42, command.revision());
+    }
+
+    @Test
+    void testGetCommand() {
+        GetCommand command = decodeCommand("cBUDBWtleTEr");
+
+        assertEquals(42, command.revision());
+        assertEquals(key("key1"), command.key());
+    }
+
+    @Test
+    void testGetCurrentRevisionsCommand() {
+        Command command = decodeCommand("cCI=");
+
+        assertInstanceOf(GetCurrentRevisionsCommand.class, command);
+    }
+
+    @Test
+    void testGetPrefixCommand() {
+        GetPrefixCommand command = 
decodeCommand("cD5lAQMIcHJlZml4MQ1wcmV2aW91c0tleTEr");
+
+        assertEquals(key("prefix1"), command.prefix());
+        assertEquals(100, command.batchSize());
+        assertTrue(command.includeTombstones());
+        assertArrayEquals(keyBytes("previousKey1"), command.previousKey());
+        assertEquals(42, command.revUpperBound());
+    }
+
+    @Test
+    void testGetRangeCommand() {
+        GetRangeCommand command = 
decodeCommand("cD1lAQMJa2V5RnJvbTEDB2tleVRvMQ1wcmV2aW91c0tleTEr");
+
+        assertEquals(key("keyFrom1"), command.keyFrom());
+        assertEquals(key("keyTo1"), command.keyTo());
+        assertEquals(100, command.batchSize());
+        assertTrue(command.includeTombstones());
+        assertArrayEquals(keyBytes("previousKey1"), command.previousKey());
+        assertEquals(42, command.revUpperBound());
+    }
+
+    @Test
+    void testInvokeCommand() {
+        InvokeCommand command = decodeCommand(
+                
"cAvfAQIDCGV4aXN0czEOAt8BBgMIcmVtb3ZlMQQA3wEMRwAqAAAAAAAAAEUAAAAAAAAAR0YC3wEGAwVwdXQxAwMFdmFsMQ=="
+        );
+
+        assertEquals(safeTime(), command.safeTime());
+        assertEquals(initiatorTime(), command.initiatorTime());
+        assertEquals(commandId(), command.id());
+        assertEquals(Conditions.exists(keyAsByteArray("exists1")), 
command.condition());
+        assertEquals(
+                List.of(Operations.put(keyAsByteArray("put1"), 
keyBytes("val1"))),
+                command.success()
+        );
+        assertEquals(
+                List.of(Operations.remove(keyAsByteArray("remove1"))),
+                command.failure()
+        );
+    }
+
+    @Test
+    @Disabled("https://issues.apache.org/jira/browse/IGNITE-26664";)
+    void testMultiInvokeCommand() {
+        MultiInvokeCommand command = decodeCommand(
+                
"cAzfAQxHACoAAAAAAAAARQAAAAAAAADfAQnfAQvfAQgC3wEGAwVwdXQxAwMFdmFsMd8BBwMCAd8BBd8BBd8BAgMLdG9tYnN0b25lMRDfAQIDDm5vdFRvbWJzd"
+                        + 
"G9uZTERAt8BBd8BBd8BAgMIZXhpc3RzMQ7fAQIDC25vdEV4aXN0czEPA98BBd8BAwMKcmV2aXNpb24xAgLfAQQDB3ZhbHVlMQkDBXZhbDECAwLf"
+                        + "AQvfAQgC3wEGAAIA3wEHAwIAR0Y="
+        );
+
+        Condition tombstonesConditions = Conditions.and(
+                Conditions.tombstone(keyAsByteArray("tombstone1")),
+                Conditions.notTombstone(keyAsByteArray("notTombstone1"))
+        );
+
+        Condition existsConditions = Conditions.or(
+                Conditions.exists(keyAsByteArray("exists1")),
+                Conditions.notExists(keyAsByteArray("notExists1")
+                ));
+
+        Condition revisionAndValueConsitions = Conditions.and(
+                Conditions.revision(keyAsByteArray("revision1")).eq(1L),
+                Conditions.value(keyAsByteArray("value1")).ne(keyBytes("val1"))
+        );
+
+        Condition complexCondition = Conditions.and(
+                tombstonesConditions,
+                Conditions.or(existsConditions, revisionAndValueConsitions)
+        );
+
+        Iif iif = Statements.iif(
+                complexCondition,
+                Operations.ops(Operations.put(keyAsByteArray("put1"), 
keyBytes("val1"))).yield(true),
+                Operations.ops(Operations.noop()).yield(false)
+        );
+
+        assertEquals(safeTime(), command.safeTime());
+        assertEquals(initiatorTime(), command.initiatorTime());
+        assertEquals(commandId(), command.id());
+        assertEquals(iif, command.iif());
+    }
+
+    @Test
+    void testPutAllCommand() {
+        PutAllCommand command = 
decodeCommand("cDNHAwMFa2V5MQMFa2V5MkYDAwV2YWwxAwV2YWwy");
+
+        assertEquals(safeTime(), command.safeTime());
+        assertEquals(initiatorTime(), command.initiatorTime());
+        assertEquals(List.of(key("key1"), key("key2")), command.keys());
+        assertEquals(List.of(key("val1"), key("val2")), command.values());
+    }
+
+    @Test
+    void testPutCommand() {
+        PutCommand command = decodeCommand("cClHAwVrZXkxRgMFdmFsMQ==");
+
+        assertEquals(safeTime(), command.safeTime());
+        assertEquals(initiatorTime(), command.initiatorTime());
+        assertEquals(key("key1"), command.key());
+        assertEquals(key("val1"), command.value());
+    }
+
+    @Test
+    void testRemoveAllCommand() {
+        RemoveAllCommand command = decodeCommand("cDRHAwMFa2V5MQMFa2V5MkY=");
+
+        assertEquals(safeTime(), command.safeTime());
+        assertEquals(initiatorTime(), command.initiatorTime());
+        assertEquals(List.of(key("key1"), key("key2")), command.keys());
+    }
+
+    @Test
+    void testRemoveByPrefixCommand() {
+        RemoveByPrefixCommand command = decodeCommand("cDVHAwhwcmVmaXgxRg==");
+
+        assertEquals(safeTime(), command.safeTime());
+        assertEquals(initiatorTime(), command.initiatorTime());
+        assertEquals(key("prefix1"), command.prefix());
+    }
+
+    @Test
+    void testRemoveCommand() {
+        RemoveCommand command = decodeCommand("cCpHAwVrZXkxRg==");
+
+        assertEquals(safeTime(), command.safeTime());
+        assertEquals(initiatorTime(), command.initiatorTime());
+        assertEquals(key("key1"), command.key());
+    }
+
+    @Test
+    void testSyncTimeCommand() {
+        SyncTimeCommand command = decodeCommand("cEcrR0Y=");
+
+        assertEquals(safeTime(), command.safeTime());
+        assertEquals(initiatorTime(), command.initiatorTime());
+        assertEquals(42, command.initiatorTerm());
+    }
+
+    private static HybridTimestamp initiatorTime() {
+        return HybridTimestamp.hybridTimestamp(70);
+    }
+
+    private static HybridTimestamp safeTime() {
+        return HybridTimestamp.hybridTimestamp(69);
+    }
+
+    private static UUID uuid() {
+        return new UUID(42, 69);
+    }
+
+    private static CommandId commandId() {
+        return CommandId.fromString(uuid() + "_cnt_" + 70);
+    }
+
+    private static ByteBuffer key(String key) {
+        return ByteBuffer.wrap(keyBytes(key));
+    }
+
+    private static byte[] keyBytes(String key) {
+        return key.getBytes(UTF_8);
+    }
+
+    private static ByteArray keyAsByteArray(String key) {
+        return ByteArray.fromString(key);
+    }
+
+    private static <T extends Command> T deserializeCommand(byte[] bytes) {
+        return MARSHALLER.unmarshall(bytes);
+    }
+
+    private static <T extends Command> T decodeCommand(String base64) {
+        return deserializeCommand(Base64.getDecoder().decode(base64));
+    }
+}

Reply via email to