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

gnodet pushed a commit to branch keyvaluerepository-spi-improvements
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 48cb1774aee45fcb20104d26f4867645a7005f16
Author: Guillaume Nodet <[email protected]>
AuthorDate: Tue Sep 1 13:28:12 2026 +0200

    CAMEL-24463: Improve KeyValueRepository SPI
    
    - Change TTL parameter type from Duration to long millis for simpler
      implementation across all backends (JDBC, JPA, Cassandra, Kafka)
    - Add put(key, value) and putIfAbsent(key, value) convenience defaults
      that delegate to the TTL variants with 0
    - Remove replace() and delete(key, expectedValue) CAS methods that no
      current implementation overrides with a real atomic operation
    - Extract shared serialization logic into KeyValueRepositoryHelper to
      eliminate duplication across persistent implementations
    - Update adapters and tests to use the simplified API
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
---
 .../org/apache/camel/spi/KeyValueRepository.java   |  90 ++++++--------
 .../support/KeyValueAggregationRepository.java     |   4 +-
 .../support/KeyValueIdempotentRepository.java      |   2 +-
 .../camel/support/KeyValueRepositoryHelper.java    | 125 ++++++++++++++++++++
 .../camel/support/MemoryKeyValueRepository.java    |  44 +------
 .../support/KeyValueIdempotentRepositoryTest.java  |   4 +-
 .../support/MemoryKeyValueRepositoryTest.java      | 131 +++++----------------
 7 files changed, 196 insertions(+), 204 deletions(-)

diff --git 
a/core/camel-api/src/main/java/org/apache/camel/spi/KeyValueRepository.java 
b/core/camel-api/src/main/java/org/apache/camel/spi/KeyValueRepository.java
index fef877edda4c..299f095490df 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/KeyValueRepository.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/KeyValueRepository.java
@@ -16,8 +16,6 @@
  */
 package org.apache.camel.spi;
 
-import java.time.Duration;
-import java.util.Objects;
 import java.util.Set;
 
 import org.apache.camel.Service;
@@ -35,8 +33,8 @@ import org.jspecify.annotations.Nullable;
  * storage technology (Redis, Hazelcast, Infinispan, JDBC, etc.), a single 
{@code KeyValueRepository} implementation can
  * be wrapped by the appropriate adapter.
  * <p/>
- * Implementations must be thread-safe. Entries may optionally have a 
time-to-live (TTL); a {@code null}, zero, or
- * negative TTL means the entry does not expire.
+ * Implementations must be thread-safe. Entries may optionally have a 
time-to-live (TTL); a TTL of {@code 0} or less
+ * means the entry does not expire.
  *
  * @since 4.23
  */
@@ -52,15 +50,27 @@ public interface KeyValueRepository extends Service {
     Object get(String key);
 
     /**
-     * Stores a value under the given key with an optional time-to-live.
+     * Stores a value under the given key with no expiration.
      *
      * @param  key   the key
      * @param  value the value to store
-     * @param  ttl   the time-to-live; {@code null}, zero, or negative means 
no expiration
      * @return       the previous value associated with the key, or {@code 
null} if there was no mapping
      */
     @Nullable
-    Object put(String key, Object value, @Nullable Duration ttl);
+    default Object put(String key, Object value) {
+        return put(key, value, 0);
+    }
+
+    /**
+     * Stores a value under the given key with an optional time-to-live.
+     *
+     * @param  key       the key
+     * @param  value     the value to store
+     * @param  ttlMillis the time-to-live in milliseconds; {@code 0} or 
negative means no expiration
+     * @return           the previous value associated with the key, or {@code 
null} if there was no mapping
+     */
+    @Nullable
+    Object put(String key, Object value, long ttlMillis);
 
     /**
      * Removes the entry for the given key.
@@ -92,68 +102,38 @@ public interface KeyValueRepository extends Service {
     void clear();
 
     /**
-     * Stores the value under the given key only if no non-expired mapping 
already exists.
+     * Stores the value under the given key only if no non-expired mapping 
already exists, with no expiration.
      * <p/>
-     * The default implementation is not atomic. Implementations backed by 
stores that support atomic compare-and-set
-     * operations should override this method for better concurrency 
guarantees.
+     * Equivalent to {@code putIfAbsent(key, value, 0)}.
      *
      * @param  key   the key
      * @param  value the value to store
-     * @param  ttl   the time-to-live; {@code null}, zero, or negative means 
no expiration
      * @return       the existing value if the key was already present, or 
{@code null} if the put succeeded
      */
     @Nullable
-    default Object putIfAbsent(String key, Object value, @Nullable Duration 
ttl) {
-        Object existing = get(key);
-        if (existing != null) {
-            return existing;
-        }
-        put(key, value, ttl);
-        return null;
+    default Object putIfAbsent(String key, Object value) {
+        return putIfAbsent(key, value, 0);
     }
 
     /**
-     * Atomically replaces the value for the given key only if the current 
value equals the expected old value
-     * (compare-and-swap).
-     * <p/>
-     * The default implementation is not atomic. Implementations backed by 
stores that support atomic compare-and-swap
-     * operations (e.g., {@code ConcurrentMap.replace}, Hazelcast {@code 
IMap.replace}) should override this method for
-     * better concurrency guarantees.
-     *
-     * @param  key              the key
-     * @param  expectedOldValue the value that must currently be associated 
with the key
-     * @param  newValue         the new value to store
-     * @param  ttl              the time-to-live for the new entry; {@code 
null}, zero, or negative means no expiration
-     * @return                  {@code true} if the value was replaced, {@code 
false} if the current value did not match
-     */
-    default boolean replace(String key, Object expectedOldValue, Object 
newValue, @Nullable Duration ttl) {
-        Object current = get(key);
-        if (current != null && Objects.equals(current, expectedOldValue)) {
-            put(key, newValue, ttl);
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Removes the entry for the given key only if the current value equals 
the expected value (compare-and-swap).
+     * Stores the value under the given key only if no non-expired mapping 
already exists.
      * <p/>
-     * The default implementation is not atomic. Implementations backed by 
stores that support atomic compare-and-remove
-     * operations (e.g., {@code ConcurrentMap.remove(key, value)}, Hazelcast 
{@code IMap.remove(key, value)}) should
-     * override this method for better concurrency guarantees.
+     * The default implementation is not atomic. Implementations backed by 
stores that support atomic compare-and-set
+     * operations should override this method for better concurrency 
guarantees.
      *
-     * @param  key           the key to remove
-     * @param  expectedValue the value that must currently be associated with 
the key
-     * @return               {@code true} if the entry was removed, {@code 
false} if the current value did not match or
-     *                       the key was not present
+     * @param  key       the key
+     * @param  value     the value to store
+     * @param  ttlMillis the time-to-live in milliseconds; {@code 0} or 
negative means no expiration
+     * @return           the existing value if the key was already present, or 
{@code null} if the put succeeded
      */
-    default boolean delete(String key, Object expectedValue) {
-        Object current = get(key);
-        if (current != null && Objects.equals(current, expectedValue)) {
-            delete(key);
-            return true;
+    @Nullable
+    default Object putIfAbsent(String key, Object value, long ttlMillis) {
+        Object existing = get(key);
+        if (existing != null) {
+            return existing;
         }
-        return false;
+        put(key, value, ttlMillis);
+        return null;
     }
 
     /**
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/KeyValueAggregationRepository.java
 
b/core/camel-support/src/main/java/org/apache/camel/support/KeyValueAggregationRepository.java
index 95fb22141b5b..38a4aaf24301 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/KeyValueAggregationRepository.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/KeyValueAggregationRepository.java
@@ -110,7 +110,7 @@ public class KeyValueAggregationRepository extends 
ServiceSupport
     public Exchange add(CamelContext camelContext, String key, Exchange 
exchange) {
         LOG.trace("Adding an Exchange with ID {} for key {}", 
exchange.getExchangeId(), key);
         DefaultExchangeHolder newHolder = 
DefaultExchangeHolder.marshal(exchange, true, allowSerializedHeaders);
-        DefaultExchangeHolder oldHolder = (DefaultExchangeHolder) 
repository.put(AGGREGATE_PREFIX + key, newHolder, null);
+        DefaultExchangeHolder oldHolder = (DefaultExchangeHolder) 
repository.put(AGGREGATE_PREFIX + key, newHolder);
         return unmarshallExchange(camelContext, oldHolder);
     }
 
@@ -126,7 +126,7 @@ public class KeyValueAggregationRepository extends 
ServiceSupport
         if (useRecovery && holder != null) {
             // Store under the exchangeId for potential recovery
             LOG.trace("Moving Exchange with ID {} to completed (pending 
confirmation)", exchange.getExchangeId());
-            repository.put(COMPLETED_PREFIX + exchange.getExchangeId(), 
holder, null);
+            repository.put(COMPLETED_PREFIX + exchange.getExchangeId(), 
holder);
         }
     }
 
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/KeyValueIdempotentRepository.java
 
b/core/camel-support/src/main/java/org/apache/camel/support/KeyValueIdempotentRepository.java
index d11b656641e1..02d87434d725 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/KeyValueIdempotentRepository.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/KeyValueIdempotentRepository.java
@@ -84,7 +84,7 @@ public class KeyValueIdempotentRepository extends 
ServiceSupport implements Idem
     @Override
     public boolean add(String key) {
         // putIfAbsent returns null if the key was successfully added (not 
already present)
-        return repository.putIfAbsent(IDEMPOTENT_PREFIX + key, Boolean.TRUE, 
null) == null;
+        return repository.putIfAbsent(IDEMPOTENT_PREFIX + key, Boolean.TRUE) 
== null;
     }
 
     @Override
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/KeyValueRepositoryHelper.java
 
b/core/camel-support/src/main/java/org/apache/camel/support/KeyValueRepositoryHelper.java
new file mode 100644
index 000000000000..13d3ff696456
--- /dev/null
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/KeyValueRepositoryHelper.java
@@ -0,0 +1,125 @@
+/*
+ * 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.camel.support;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.nio.ByteBuffer;
+
+import org.apache.camel.RuntimeCamelException;
+
+/**
+ * Shared serialization utilities for {@link 
org.apache.camel.spi.KeyValueRepository} implementations.
+ * <p/>
+ * All persistent {@code KeyValueRepository} implementations need to serialize 
arbitrary Java objects to bytes (for BLOB
+ * columns, Kafka messages, etc.) and deserialize them back. This helper 
centralises that logic to avoid the same
+ * try/catch boilerplate in every implementation.
+ * <p/>
+ * <b>Security note:</b> These methods use plain Java serialization
+ * ({@link ObjectOutputStream}/{@link ObjectInputStream}). The stored data is 
trusted — it was written by the same
+ * application instance or cluster. Do not expose a repository's raw byte 
store to untrusted input.
+ *
+ * @since 4.23
+ */
+public final class KeyValueRepositoryHelper {
+
+    private KeyValueRepositoryHelper() {
+        // utility class
+    }
+
+    /**
+     * Serializes an object to a byte array using Java object serialization.
+     *
+     * @param  value                 the object to serialize (must be {@link 
java.io.Serializable})
+     * @return                       the serialized bytes
+     * @throws RuntimeCamelException if serialization fails
+     */
+    public static byte[] serialize(Object value) {
+        try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
+             ObjectOutputStream oos = new ObjectOutputStream(bos)) {
+            oos.writeObject(value);
+            oos.flush();
+            return bos.toByteArray();
+        } catch (IOException e) {
+            throw new RuntimeCamelException("Failed to serialize value", e);
+        }
+    }
+
+    /**
+     * Serializes an object to a {@link ByteBuffer} using Java object 
serialization. Useful for drivers that work with
+     * {@code ByteBuffer} (e.g. Cassandra).
+     *
+     * @param  value                 the object to serialize (must be {@link 
java.io.Serializable})
+     * @return                       a ByteBuffer wrapping the serialized bytes
+     * @throws RuntimeCamelException if serialization fails
+     */
+    public static ByteBuffer serializeToByteBuffer(Object value) {
+        return ByteBuffer.wrap(serialize(value));
+    }
+
+    /**
+     * Deserializes a byte array back into an object using Java object 
serialization.
+     *
+     * @param  bytes                 the bytes to deserialize
+     * @return                       the deserialized object
+     * @throws RuntimeCamelException if deserialization fails
+     */
+    public static Object deserialize(byte[] bytes) {
+        try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
+             ObjectInputStream ois = new ObjectInputStream(bis)) {
+            return ois.readObject();
+        } catch (IOException | ClassNotFoundException e) {
+            throw new RuntimeCamelException("Failed to deserialize value", e);
+        }
+    }
+
+    /**
+     * Deserializes an object from a portion of a byte array using Java object 
serialization. Useful when the serialized
+     * data starts at an offset (e.g. after a protocol header).
+     *
+     * @param  bytes                 the byte array containing the serialized 
data
+     * @param  offset                the start offset within the array
+     * @param  length                the number of bytes to read
+     * @return                       the deserialized object
+     * @throws RuntimeCamelException if deserialization fails
+     */
+    public static Object deserialize(byte[] bytes, int offset, int length) {
+        try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes, 
offset, length);
+             ObjectInputStream ois = new ObjectInputStream(bis)) {
+            return ois.readObject();
+        } catch (IOException | ClassNotFoundException e) {
+            throw new RuntimeCamelException("Failed to deserialize value", e);
+        }
+    }
+
+    /**
+     * Deserializes an object from a {@link ByteBuffer} using Java object 
serialization. The buffer's remaining bytes
+     * are consumed.
+     *
+     * @param  buffer                the ByteBuffer containing the serialized 
bytes
+     * @return                       the deserialized object
+     * @throws RuntimeCamelException if deserialization fails
+     */
+    public static Object deserialize(ByteBuffer buffer) {
+        byte[] bytes = new byte[buffer.remaining()];
+        buffer.get(bytes);
+        return deserialize(bytes);
+    }
+}
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/MemoryKeyValueRepository.java
 
b/core/camel-support/src/main/java/org/apache/camel/support/MemoryKeyValueRepository.java
index d6ce48680ca9..f32527ea9094 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/MemoryKeyValueRepository.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/MemoryKeyValueRepository.java
@@ -18,10 +18,8 @@ package org.apache.camel.support;
 
 import java.io.Serial;
 import java.io.Serializable;
-import java.time.Duration;
 import java.util.Iterator;
 import java.util.Map;
-import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.stream.Collectors;
@@ -75,8 +73,8 @@ public class MemoryKeyValueRepository extends ServiceSupport 
implements KeyValue
 
     @Override
     @ManagedOperation(description = "Put a key-value pair with optional TTL")
-    public Object put(String key, Object value, Duration ttl) {
-        long expiresAt = toExpiresAt(ttl);
+    public Object put(String key, Object value, long ttlMillis) {
+        long expiresAt = ttlMillis > 0 ? System.currentTimeMillis() + 
ttlMillis : Long.MAX_VALUE;
         Entry previous = store.put(key, new Entry(value, expiresAt));
         if (previous == null) {
             return null;
@@ -132,8 +130,8 @@ public class MemoryKeyValueRepository extends 
ServiceSupport implements KeyValue
     }
 
     @Override
-    public Object putIfAbsent(String key, Object value, Duration ttl) {
-        long expiresAt = toExpiresAt(ttl);
+    public Object putIfAbsent(String key, Object value, long ttlMillis) {
+        long expiresAt = ttlMillis > 0 ? System.currentTimeMillis() + 
ttlMillis : Long.MAX_VALUE;
         Entry newEntry = new Entry(value, expiresAt);
         Entry existing = store.putIfAbsent(key, newEntry);
         if (existing == null) {
@@ -151,33 +149,6 @@ public class MemoryKeyValueRepository extends 
ServiceSupport implements KeyValue
         return existing.value();
     }
 
-    @Override
-    public boolean replace(String key, Object expectedOldValue, Object 
newValue, Duration ttl) {
-        long expiresAt = toExpiresAt(ttl);
-        boolean[] replaced = { false };
-        store.computeIfPresent(key, (k, current) -> {
-            if (!current.isExpired() && Objects.equals(current.value(), 
expectedOldValue)) {
-                replaced[0] = true;
-                return new Entry(newValue, expiresAt);
-            }
-            return current;
-        });
-        return replaced[0];
-    }
-
-    @Override
-    public boolean delete(String key, Object expectedValue) {
-        boolean[] removed = { false };
-        store.computeIfPresent(key, (k, current) -> {
-            if (!current.isExpired() && Objects.equals(current.value(), 
expectedValue)) {
-                removed[0] = true;
-                return null; // returning null removes the entry from the map
-            }
-            return current;
-        });
-        return removed[0];
-    }
-
     @Override
     @ManagedAttribute(description = "The number of entries in the repository")
     public int size() {
@@ -190,13 +161,6 @@ public class MemoryKeyValueRepository extends 
ServiceSupport implements KeyValue
         store.clear();
     }
 
-    private static long toExpiresAt(Duration ttl) {
-        if (ttl == null || ttl.isZero() || ttl.isNegative()) {
-            return Long.MAX_VALUE;
-        }
-        return System.currentTimeMillis() + ttl.toMillis();
-    }
-
     private void evictExpired() {
         Iterator<Map.Entry<String, Entry>> it = store.entrySet().iterator();
         while (it.hasNext()) {
diff --git 
a/core/camel-support/src/test/java/org/apache/camel/support/KeyValueIdempotentRepositoryTest.java
 
b/core/camel-support/src/test/java/org/apache/camel/support/KeyValueIdempotentRepositoryTest.java
index c1853e4c172a..3b78af19a580 100644
--- 
a/core/camel-support/src/test/java/org/apache/camel/support/KeyValueIdempotentRepositoryTest.java
+++ 
b/core/camel-support/src/test/java/org/apache/camel/support/KeyValueIdempotentRepositoryTest.java
@@ -145,7 +145,7 @@ class KeyValueIdempotentRepositoryTest {
     @Test
     void testClearDoesNotAffectOtherPrefixes() {
         // Simulate another adapter storing entries under a different prefix
-        kvRepository.put("aggregate:order-1", "exchange-holder", null);
+        kvRepository.put("aggregate:order-1", "exchange-holder");
 
         // Add idempotent entries and clear them
         idempotentRepository.add("msg-001");
@@ -166,7 +166,7 @@ class KeyValueIdempotentRepositoryTest {
         idempotentRepository.add("order-1");
 
         // A different adapter storing under its own prefix should not collide
-        kvRepository.put("aggregate:order-1", "exchange-data", null);
+        kvRepository.put("aggregate:order-1", "exchange-data");
 
         // The idempotent entry should still resolve correctly
         assertThat(idempotentRepository.contains("order-1")).isTrue();
diff --git 
a/core/camel-support/src/test/java/org/apache/camel/support/MemoryKeyValueRepositoryTest.java
 
b/core/camel-support/src/test/java/org/apache/camel/support/MemoryKeyValueRepositoryTest.java
index e72a8f71ae26..f8f9465cb255 100644
--- 
a/core/camel-support/src/test/java/org/apache/camel/support/MemoryKeyValueRepositoryTest.java
+++ 
b/core/camel-support/src/test/java/org/apache/camel/support/MemoryKeyValueRepositoryTest.java
@@ -16,7 +16,6 @@
  */
 package org.apache.camel.support;
 
-import java.time.Duration;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
@@ -44,7 +43,7 @@ class MemoryKeyValueRepositoryTest {
 
     @Test
     void testPutAndGet() {
-        repository.put("key1", "value1", null);
+        repository.put("key1", "value1", 0);
 
         assertThat(repository.get("key1")).isEqualTo("value1");
     }
@@ -56,15 +55,15 @@ class MemoryKeyValueRepositoryTest {
 
     @Test
     void testPutOverwritesExistingValue() {
-        repository.put("key1", "value1", null);
-        repository.put("key1", "value2", null);
+        repository.put("key1", "value1", 0);
+        repository.put("key1", "value2", 0);
 
         assertThat(repository.get("key1")).isEqualTo("value2");
     }
 
     @Test
     void testDelete() {
-        repository.put("key1", "value1", null);
+        repository.put("key1", "value1", 0);
 
         Object deleted = repository.delete("key1");
 
@@ -79,7 +78,7 @@ class MemoryKeyValueRepositoryTest {
 
     @Test
     void testContains() {
-        repository.put("key1", "value1", null);
+        repository.put("key1", "value1", 0);
 
         assertThat(repository.contains("key1")).isTrue();
         assertThat(repository.contains("nonexistent")).isFalse();
@@ -87,9 +86,9 @@ class MemoryKeyValueRepositoryTest {
 
     @Test
     void testKeys() {
-        repository.put("key1", "value1", null);
-        repository.put("key2", "value2", null);
-        repository.put("key3", "value3", null);
+        repository.put("key1", "value1", 0);
+        repository.put("key2", "value2", 0);
+        repository.put("key3", "value3", 0);
 
         Set<String> keys = repository.keys();
 
@@ -103,8 +102,8 @@ class MemoryKeyValueRepositoryTest {
 
     @Test
     void testClear() {
-        repository.put("key1", "value1", null);
-        repository.put("key2", "value2", null);
+        repository.put("key1", "value1", 0);
+        repository.put("key2", "value2", 0);
 
         repository.clear();
 
@@ -117,10 +116,10 @@ class MemoryKeyValueRepositoryTest {
     void testSize() {
         assertThat(repository.size()).isZero();
 
-        repository.put("key1", "value1", null);
+        repository.put("key1", "value1", 0);
         assertThat(repository.size()).isEqualTo(1);
 
-        repository.put("key2", "value2", null);
+        repository.put("key2", "value2", 0);
         assertThat(repository.size()).isEqualTo(2);
 
         repository.delete("key1");
@@ -129,7 +128,7 @@ class MemoryKeyValueRepositoryTest {
 
     @Test
     void testPutIfAbsentNewKey() {
-        Object result = repository.putIfAbsent("key1", "value1", null);
+        Object result = repository.putIfAbsent("key1", "value1", 0);
 
         assertThat(result).isNull();
         assertThat(repository.get("key1")).isEqualTo("value1");
@@ -137,9 +136,9 @@ class MemoryKeyValueRepositoryTest {
 
     @Test
     void testPutIfAbsentExistingKey() {
-        repository.put("key1", "value1", null);
+        repository.put("key1", "value1", 0);
 
-        Object result = repository.putIfAbsent("key1", "value2", null);
+        Object result = repository.putIfAbsent("key1", "value2", 0);
 
         assertThat(result).isEqualTo("value1");
         assertThat(repository.get("key1")).isEqualTo("value1");
@@ -148,7 +147,7 @@ class MemoryKeyValueRepositoryTest {
     @Test
     void testTtlExpiration() {
         // Use a very short TTL
-        repository.put("key1", "value1", Duration.ofMillis(50));
+        repository.put("key1", "value1", 50);
 
         assertThat(repository.get("key1")).isEqualTo("value1");
         assertThat(repository.contains("key1")).isTrue();
@@ -163,8 +162,8 @@ class MemoryKeyValueRepositoryTest {
 
     @Test
     void testTtlExpirationOnKeys() {
-        repository.put("key1", "value1", Duration.ofMillis(50));
-        repository.put("key2", "value2", null); // no expiration
+        repository.put("key1", "value1", 50);
+        repository.put("key2", "value2", 0); // no expiration
 
         await().atMost(500, TimeUnit.MILLISECONDS)
                 .untilAsserted(() -> {
@@ -175,7 +174,7 @@ class MemoryKeyValueRepositoryTest {
 
     @Test
     void testTtlExpirationOnDelete() {
-        repository.put("key1", "value1", Duration.ofMillis(50));
+        repository.put("key1", "value1", 50);
 
         await().atMost(500, TimeUnit.MILLISECONDS)
                 .untilAsserted(() -> {
@@ -186,38 +185,29 @@ class MemoryKeyValueRepositoryTest {
 
     @Test
     void testPutIfAbsentWithExpiredEntry() {
-        repository.put("key1", "value1", Duration.ofMillis(50));
+        repository.put("key1", "value1", 50);
 
         await().atMost(500, TimeUnit.MILLISECONDS)
                 .untilAsserted(() -> {
                     // The entry has expired, so putIfAbsent should succeed
-                    Object result = repository.putIfAbsent("key1", "value2", 
null);
+                    Object result = repository.putIfAbsent("key1", "value2", 
0);
                     assertThat(result).isNull();
                     assertThat(repository.get("key1")).isEqualTo("value2");
                 });
     }
 
-    @Test
-    void testNoTtlWithNull() {
-        repository.put("key1", "value1", null);
-
-        // Entry with null TTL should not expire
-        assertThat(repository.get("key1")).isEqualTo("value1");
-        assertThat(repository.contains("key1")).isTrue();
-    }
-
     @Test
     void testNoTtlWithZero() {
-        repository.put("key1", "value1", Duration.ZERO);
+        repository.put("key1", "value1", 0);
 
-        // Entry with zero TTL should not expire
+        // Entry with TTL=0 should not expire
         assertThat(repository.get("key1")).isEqualTo("value1");
         assertThat(repository.contains("key1")).isTrue();
     }
 
     @Test
     void testNoTtlWithNegative() {
-        repository.put("key1", "value1", Duration.ofMillis(-1));
+        repository.put("key1", "value1", -1);
 
         // Entry with negative TTL should not expire
         assertThat(repository.get("key1")).isEqualTo("value1");
@@ -226,79 +216,12 @@ class MemoryKeyValueRepositoryTest {
 
     @Test
     void testStoresDifferentValueTypes() {
-        repository.put("string", "hello", null);
-        repository.put("integer", 42, null);
-        repository.put("boolean", Boolean.TRUE, null);
+        repository.put("string", "hello", 0);
+        repository.put("integer", 42, 0);
+        repository.put("boolean", Boolean.TRUE, 0);
 
         assertThat(repository.get("string")).isEqualTo("hello");
         assertThat(repository.get("integer")).isEqualTo(42);
         assertThat(repository.get("boolean")).isEqualTo(Boolean.TRUE);
     }
-
-    @Test
-    void testReplaceMatchingValue() {
-        repository.put("key1", "value1", null);
-
-        boolean replaced = repository.replace("key1", "value1", "value2", 
null);
-
-        assertThat(replaced).isTrue();
-        assertThat(repository.get("key1")).isEqualTo("value2");
-    }
-
-    @Test
-    void testReplaceNonMatchingValue() {
-        repository.put("key1", "value1", null);
-
-        boolean replaced = repository.replace("key1", "wrong", "value2", null);
-
-        assertThat(replaced).isFalse();
-        assertThat(repository.get("key1")).isEqualTo("value1");
-    }
-
-    @Test
-    void testReplaceMissingKey() {
-        boolean replaced = repository.replace("nonexistent", "value1", 
"value2", null);
-
-        assertThat(replaced).isFalse();
-    }
-
-    @Test
-    void testReplaceWithTtl() {
-        repository.put("key1", "value1", null);
-
-        boolean replaced = repository.replace("key1", "value1", "value2", 
Duration.ofMillis(500));
-
-        assertThat(replaced).isTrue();
-        assertThat(repository.get("key1")).isEqualTo("value2");
-
-        await().atMost(5, TimeUnit.SECONDS)
-                .untilAsserted(() -> 
assertThat(repository.get("key1")).isNull());
-    }
-
-    @Test
-    void testDeleteWithMatchingValue() {
-        repository.put("key1", "value1", null);
-
-        boolean deleted = repository.delete("key1", "value1");
-
-        assertThat(deleted).isTrue();
-        assertThat(repository.get("key1")).isNull();
-    }
-
-    @Test
-    void testDeleteWithNonMatchingValue() {
-        repository.put("key1", "value1", null);
-
-        boolean deleted = repository.delete("key1", "wrong");
-
-        assertThat(deleted).isFalse();
-        assertThat(repository.get("key1")).isEqualTo("value1");
-    }
-
-    @Test
-    void testDeleteWithMissingKey() {
-        boolean deleted = repository.delete("nonexistent", "value1");
-
-        assertThat(deleted).isFalse();
-    }
 }

Reply via email to