diff --git a/local_examples/cmds_hash/NRedisStack/CmdsHashExample.cs b/local_examples/cmds_hash/NRedisStack/CmdsHashExample.cs index 038c5b5b0e..6ff69e23cf 100644 --- a/local_examples/cmds_hash/NRedisStack/CmdsHashExample.cs +++ b/local_examples/cmds_hash/NRedisStack/CmdsHashExample.cs @@ -1,6 +1,7 @@ // EXAMPLE: cmds_hash using StackExchange.Redis; using Xunit; +using System.Linq; namespace Doc; @@ -22,12 +23,14 @@ public void Run() RedisValue hdelRes3 = db.HashDelete("myhash", "field1"); Console.WriteLine(hdelRes3); // >>> 0 - // STEP_END + + // REMOVE_START Assert.True(hdelRes1); Assert.Equal(1, hdelRes2); Assert.Equal(0, hdelRes3); db.KeyDelete("myhash"); + // REMOVE_END // STEP_START hget bool hgetRes1 = db.HashSet("myhash", "field1", "foo"); @@ -37,12 +40,14 @@ public void Run() RedisValue hgetRes3 = db.HashGet("myhash", "field2"); Console.WriteLine(hgetRes3); // >>> Null - // STEP_END + + // REMOVE_START Assert.True(hgetRes1); Assert.Equal("foo", hgetRes2); Assert.Equal(RedisValue.Null, hgetRes3); db.KeyDelete("myhash"); + // REMOVE_END // STEP_START hset bool hsetRes1 = db.HashSet("myhash", "field1", "Hello"); @@ -66,8 +71,9 @@ public void Run() HashEntry[] hsetRes5 = db.HashGetAll("myhash"); Console.WriteLine($"{string.Join(", ", hsetRes5.Select(h => $"{h.Name}: {h.Value}"))}"); // >>> field1: Hello, field2: Hi, field3: World - // STEP_END + + // REMOVE_START Assert.True(hsetRes1); Assert.Equal("Hello", hsetRes2); Assert.Equal("Hi", hsetRes3); @@ -77,6 +83,7 @@ public void Run() string.Join(", ", hsetRes5.Select(h => $"{h.Name}: {h.Value}")) ); db.KeyDelete("myhash"); + // REMOVE_END // STEP_START hgetall db.HashSet("myhash", @@ -93,8 +100,11 @@ public void Run() ); // >>> field1: Hello, field2: World // STEP_END + + // REMOVE_START Assert.Equal("field1: Hello, field2: World", string.Join(", ", hGetAllResult.Select(e => $"{e.Name}: {e.Value}"))); db.KeyDelete("myhash"); + // REMOVE_END // STEP_START hvals db.HashSet("myhash", @@ -109,6 +119,51 @@ public void Run() Console.WriteLine(string.Join(", ", hValsResult)); // >>> Hello, World // STEP_END + + // REMOVE_START Assert.Equal("Hello, World", string.Join(", ", hValsResult)); + db.KeyDelete("myhash"); + // REMOVE_END + + // STEP_START hexpire + // Set up hash with fields + db.HashSet("myhash", + [ + new("field1", "Hello"), + new("field2", "World") + ] + ); + + ExpireResult[] hexpireRes1 = db.HashFieldExpire( + "myhash", + new RedisValue[] { "field1", "field2" }, + TimeSpan.FromSeconds(10) + ); + Console.WriteLine(string.Join(", ", hexpireRes1)); + // >>> Success, Success + + long[] hexpireRes2 = db.HashFieldGetTimeToLive( + "myhash", + new RedisValue[] { "field1", "field2" } + ); + Console.WriteLine(string.Join(", ", hexpireRes2)); + // >>> 10, 10 (approximately) + + // Try to set expiration on non-existent field + ExpireResult[] hexpireRes3 = db.HashFieldExpire( + "myhash", + new RedisValue[] { "nonexistent" }, + TimeSpan.FromSeconds(10) + ); + Console.WriteLine(string.Join(", ", hexpireRes3)); + // >>> NoSuchField + // STEP_END + // REMOVE_START + Assert.Equal("Success, Success", string.Join(", ", hexpireRes1)); + Assert.Equal(2, hexpireRes2.Length); + Assert.True(hexpireRes2.All(ttl => ttl > 0)); // TTL should be positive + Assert.Equal("NoSuchField", string.Join(", ", hexpireRes3)); + db.KeyDelete("myhash"); + // REMOVE_END } } diff --git a/local_examples/cmds_hash/go-redis/cmds_hash_test.go b/local_examples/cmds_hash/go-redis/cmds_hash_test.go index 8dcd85d812..e2ba1e5d6b 100644 --- a/local_examples/cmds_hash/go-redis/cmds_hash_test.go +++ b/local_examples/cmds_hash/go-redis/cmds_hash_test.go @@ -6,6 +6,7 @@ import ( "context" "fmt" "sort" + "time" "github.com/redis/go-redis/v9" ) @@ -293,4 +294,54 @@ func ExampleClient_hdel() { // 1 // 1 // 0 +} + +func ExampleClient_hexpire() { + ctx := context.Background() + + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password + DB: 0, // use default DB + }) + + // STEP_START hexpire + // Set up hash with fields + rdb.HSet(ctx, "myhash", "field1", "Hello", "field2", "World") + + // Set expiration on hash fields + res1, err := rdb.HExpire(ctx, "myhash", 10*time.Second, "field1", "field2").Result() + + if err != nil { + fmt.Println(err) + } + + fmt.Println(res1) // >>> [1 1] + + // Check TTL of the fields + res2, err := rdb.HTTL(ctx, "myhash", "field1", "field2").Result() + + if err != nil { + fmt.Println(err) + } + + fmt.Println(len(res2)) // >>> 2 + + // Try to set expiration on non-existent field + res3, err := rdb.HExpire(ctx, "myhash", 10*time.Second, "nonexistent").Result() + + if err != nil { + fmt.Println(err) + } + + fmt.Println(res3) // >>> [-2] + + // Clean up + rdb.Del(ctx, "myhash") + // STEP_END + + // Output: + // [1 1] + // 2 + // [-2] } \ No newline at end of file diff --git a/local_examples/cmds_hash/jedis/CmdsHashExample.java b/local_examples/cmds_hash/jedis/CmdsHashExample.java index 3124a26d71..b52b88af2e 100644 --- a/local_examples/cmds_hash/jedis/CmdsHashExample.java +++ b/local_examples/cmds_hash/jedis/CmdsHashExample.java @@ -9,6 +9,7 @@ import java.util.Map; import java.util.List; import java.util.Collections; +import java.util.Arrays; // HIDE_START import redis.clients.jedis.UnifiedJedis; @@ -17,6 +18,7 @@ import static java.util.stream.Collectors.toList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; // HIDE_START public class CmdsHashExample { @@ -159,13 +161,41 @@ public void run() { System.out.println(hValsResult2); // >>> [Hello, World] // STEP_END - // REMOVE_START + // REMOVE_START // Tests for 'hvals' step. assertEquals(2, hValsResult1); assertEquals("[Hello, World]", hValsResult2.toString()); jedis.del("myhash"); // REMOVE_END + // STEP_START hexpire + // Set up hash with fields + Map hExpireExampleParams = new HashMap<>(); + hExpireExampleParams.put("field1", "Hello"); + hExpireExampleParams.put("field2", "World"); + jedis.hset("myhash", hExpireExampleParams); + + // Set expiration on hash fields + List hExpireResult1 = jedis.hexpire("myhash", 10, "field1", "field2"); + System.out.println(hExpireResult1); // >>> [1, 1] + + // Check TTL of the fields + List hExpireResult2 = jedis.httl("myhash", "field1", "field2"); + System.out.println(hExpireResult2.size()); // >>> 2 + + // Try to set expiration on non-existent field + List hExpireResult3 = jedis.hexpire("myhash", 10, "nonexistent"); + System.out.println(hExpireResult3); // >>> [-2] + // STEP_END + // REMOVE_START + // Tests for 'hexpire' step. + assertEquals(Arrays.asList(1L, 1L), hExpireResult1); + assertEquals(2, hExpireResult2.size()); + assertTrue(hExpireResult2.stream().allMatch(ttl -> ttl > 0)); // TTL should be positive + assertEquals(Arrays.asList(-2L), hExpireResult3); + jedis.del("myhash"); + // REMOVE_END + // HIDE_START jedis.close(); } diff --git a/local_examples/cmds_hash/lettuce-async/CmdsHashExample.java b/local_examples/cmds_hash/lettuce-async/CmdsHashExample.java index ea580b1908..e130020e7a 100644 --- a/local_examples/cmds_hash/lettuce-async/CmdsHashExample.java +++ b/local_examples/cmds_hash/lettuce-async/CmdsHashExample.java @@ -184,6 +184,52 @@ public void run() { // REMOVE_START asyncCommands.del("myhash").toCompletableFuture().join(); // REMOVE_END + + // STEP_START hexpire + // Set up hash with fields + Map hExpireExampleParams = new HashMap<>(); + hExpireExampleParams.put("field1", "Hello"); + hExpireExampleParams.put("field2", "World"); + + CompletableFuture hExpireExample = asyncCommands.hset("myhash", hExpireExampleParams).thenCompose(res1 -> { + // REMOVE_START + assertThat(res1).isEqualTo(2L); + // REMOVE_END + // Set expiration on hash fields + return asyncCommands.hexpire("myhash", 10, "field1", "field2"); + }).thenCompose(res2 -> { + System.out.println(res2); + // >>> [1, 1] + // REMOVE_START + assertThat(res2).isEqualTo(Arrays.asList(1L, 1L)); + // REMOVE_END + // Check TTL of the fields + return asyncCommands.httl("myhash", "field1", "field2"); + }).thenCompose(res3 -> { + System.out.println(res3.size()); + // >>> 2 + // REMOVE_START + assertThat(res3.size()).isEqualTo(2); + assertThat(res3.stream().allMatch(ttl -> ttl > 0)).isTrue(); // TTL should be positive + // REMOVE_END + // Try to set expiration on non-existent field + return asyncCommands.hexpire("myhash", 10, "nonexistent"); + }) + // REMOVE_START + .thenApply(res4 -> { + assertThat(res4).isEqualTo(Arrays.asList(-2L)); + return res; + }) + // REMOVE_END + .thenAccept(System.out::println) + // >>> -2 + .toCompletableFuture(); + // STEP_END + + hExpireExample.join(); + // REMOVE_START + asyncCommands.del("myhash").toCompletableFuture().join(); + // REMOVE_END } finally { redisClient.shutdown(); } diff --git a/local_examples/cmds_hash/lettuce-reactive/CmdsHashExample.java b/local_examples/cmds_hash/lettuce-reactive/CmdsHashExample.java index c42580283e..9c60b5b78a 100644 --- a/local_examples/cmds_hash/lettuce-reactive/CmdsHashExample.java +++ b/local_examples/cmds_hash/lettuce-reactive/CmdsHashExample.java @@ -224,6 +224,58 @@ public void run() { // REMOVE_START reactiveCommands.del("myhash").block(); // REMOVE_END + + // STEP_START hexpire + // Set up hash with fields + Map hExpireExampleParams = new HashMap<>(); + hExpireExampleParams.put("field1", "Hello"); + hExpireExampleParams.put("field2", "World"); + + Mono hExpireExample1 = reactiveCommands.hset("myhash", hExpireExampleParams).doOnNext(result -> { + // REMOVE_START + assertThat(result).isEqualTo(2L); + // REMOVE_END + }); + + hExpireExample1.block(); + + // Set expiration on hash fields + Mono> hExpireExample2 = reactiveCommands.hexpire("myhash", 10, "field1", "field2").collectList().doOnNext(result -> { + System.out.println(result); + // >>> [1, 1] + // REMOVE_START + assertThat(result).isEqualTo(Arrays.asList(1L, 1L)); + // REMOVE_END + }); + + hExpireExample2.block(); + + // Check TTL of the fields + Mono> hExpireExample3 = reactiveCommands.httl("myhash", "field1", "field2").collectList().doOnNext(result -> { + System.out.println(result.size()); + // >>> 2 + // REMOVE_START + assertThat(result.size()).isEqualTo(2); + assertThat(result.stream().allMatch(ttl -> ttl > 0)).isTrue(); // TTL should be positive + // REMOVE_END + }); + + hExpireExample3.block(); + + // Try to set expiration on non-existent field + Mono> hExpireExample4 = reactiveCommands.hexpire("myhash", 10, "nonexistent").collectList().doOnNext(result -> { + System.out.println(result); + // >>> [-2] + // REMOVE_START + assertThat(result).isEqualTo(Arrays.asList(-2L)); + // REMOVE_END + }); + // STEP_END + + hExpireExample4.block(); + // REMOVE_START + reactiveCommands.del("myhash").block(); + // REMOVE_END } finally { redisClient.shutdown(); } diff --git a/local_examples/cmds_hash/node-redis/cmds-hash.js b/local_examples/cmds_hash/node-redis/cmds-hash.js index e48eaad6be..8cc913b934 100644 --- a/local_examples/cmds_hash/node-redis/cmds-hash.js +++ b/local_examples/cmds_hash/node-redis/cmds-hash.js @@ -122,6 +122,33 @@ await client.del('myhash') // REMOVE_END // STEP_END +// STEP_START hexpire +// Set up hash with fields +await client.hSet('myhash', { + 'field1': 'Hello', + 'field2': 'World' +}) + +// Set expiration on hash fields +const res14 = await client.hExpire('myhash', ['field1', 'field2'], 10) +console.log(res14) // [1, 1] + +// Check TTL of the fields +const res15 = await client.hTTL('myhash', ['field1', 'field2']) +console.log(res15) // [10, 10] (or close to 10) + +// Try to set expiration on non-existent field +const res16 = await client.hExpire('myhash', ['nonexistent'], 10) +console.log(res16) // [-2] + +// REMOVE_START +assert.deepEqual(res14, [1, 1]); +assert(res15.every(ttl => ttl > 0)); // TTL should be positive +assert.deepEqual(res16, [-2]); +await client.del('myhash') +// REMOVE_END +// STEP_END + // HIDE_START await client.close(); // HIDE_END diff --git a/local_examples/cmds_hash/predis/CmdsHashTest.php b/local_examples/cmds_hash/predis/CmdsHashTest.php index 1772579f60..fa44232f05 100644 --- a/local_examples/cmds_hash/predis/CmdsHashTest.php +++ b/local_examples/cmds_hash/predis/CmdsHashTest.php @@ -26,10 +26,7 @@ protected function setUp(): void public function testCmdsHash(): void { - echo "\n=== Testing Redis Hash Commands ===\n"; - // STEP_START hdel - echo "\n--- HDEL Command ---\n"; $hDelResult1 = $this->redis->hset('myhash', 'field1', 'foo'); echo "HSET myhash field1 foo: " . $hDelResult1 . "\n"; // >>> 1 @@ -40,12 +37,13 @@ public function testCmdsHash(): void echo "HDEL myhash field2: " . $hDelResult3 . "\n"; // >>> 0 // STEP_END + // REMOVE_START $this->assertEquals(1, $hDelResult1); $this->assertEquals(1, $hDelResult2); $this->assertEquals(0, $hDelResult3); + // REMOVE_END // STEP_START hget - echo "\n--- HGET Command ---\n"; $hGetResult1 = $this->redis->hset('myhash', 'field1', 'foo'); echo "HSET myhash field1 foo: " . $hGetResult1 . "\n"; // >>> 1 @@ -56,12 +54,13 @@ public function testCmdsHash(): void echo "HGET myhash field2: " . ($hGetResult3 ?? 'null') . "\n"; // >>> null // STEP_END + // REMOVE_START $this->assertEquals(1, $hGetResult1); $this->assertEquals('foo', $hGetResult2); $this->assertNull($hGetResult3); + // REMOVE_END // STEP_START hgetall - echo "\n--- HGETALL Command ---\n"; $hGetAllResult1 = $this->redis->hmset('myhash', ['field1' => 'Hello', 'field2' => 'World']); echo "HMSET myhash field1 Hello field2 World: " . ($hGetAllResult1 ? 'OK' : 'FAIL') . "\n"; // >>> OK @@ -69,13 +68,15 @@ public function testCmdsHash(): void echo "HGETALL myhash: " . json_encode($hGetAllResult2) . "\n"; // >>> {"field1":"Hello","field2":"World"} // STEP_END + // REMOVE_START $this->assertEquals('OK', $hGetAllResult1); $this->assertEquals(['field1' => 'Hello', 'field2' => 'World'], $hGetAllResult2); + // REMOVE_END // STEP_START hset - echo "\n--- HSET Command ---\n"; - // Clean up first + // REMOVE_START $this->redis->del('myhash'); + // REMOVE_END $hSetResult1 = $this->redis->hset('myhash', 'field1', 'Hello'); echo "HSET myhash field1 Hello: " . $hSetResult1 . "\n"; // >>> 1 @@ -100,17 +101,19 @@ public function testCmdsHash(): void echo "\n"; // STEP_END + // REMOVE_START $this->assertEquals(1, $hSetResult1); $this->assertEquals('Hello', $hSetResult2); $this->assertEquals('OK', $hSetResult3); $this->assertEquals('Hi', $hSetResult4); $this->assertEquals('World', $hSetResult5); $this->assertEquals(['field1' => 'Hello', 'field2' => 'Hi', 'field3' => 'World'], $hSetResult6); + // REMOVE_END // STEP_START hvals - echo "\n--- HVALS Command ---\n"; - // Clean up first + // REMOVE_START $this->redis->del('myhash'); + // REMOVE_END $hValsResult1 = $this->redis->hmset('myhash', ['field1' => 'Hello', 'field2' => 'World']); echo "HMSET myhash field1 Hello field2 World: " . ($hValsResult1 ? 'OK' : 'FAIL') . "\n"; // >>> OK @@ -119,10 +122,40 @@ public function testCmdsHash(): void echo "HVALS myhash: " . json_encode($hValsResult2) . "\n"; // >>> ["Hello","World"] // STEP_END + // REMOVE_START $this->assertEquals('OK', $hValsResult1); $this->assertEquals(['Hello', 'World'], $hValsResult2); + // REMOVE_END + + // STEP_START hexpire + // REMOVE_START + $this->redis->del('myhash'); + // REMOVE_END + + // Set up hash with fields + $hExpireResult1 = $this->redis->hmset('myhash', ['field1' => 'Hello', 'field2' => 'World']); + echo "HMSET myhash field1 Hello field2 World: " . ($hExpireResult1 ? 'OK' : 'FAIL') . "\n"; // >>> OK + + // Set expiration on hash fields + $hExpireResult2 = $this->redis->hexpire('myhash', 10, ['field1', 'field2']); + echo "HEXPIRE myhash 10 FIELDS field1 field2: " . json_encode($hExpireResult2) . "\n"; // >>> [1,1] + + // Check TTL of the fields + $hExpireResult3 = $this->redis->httl('myhash', ['field1', 'field2']); + echo "HTTL myhash FIELDS field1 field2 count: " . count($hExpireResult3) . "\n"; // >>> 2 + + // Try to set expiration on non-existent field + $hExpireResult4 = $this->redis->hexpire('myhash', 10, ['nonexistent']); + echo "HEXPIRE myhash 10 FIELDS nonexistent: " . json_encode($hExpireResult4) . "\n"; // >>> [-2] + // STEP_END - echo "\n=== All Hash Commands Tests Passed! ===\n"; + // REMOVE_START + $this->assertEquals('OK', $hExpireResult1); + $this->assertEquals([1, 1], $hExpireResult2); + $this->assertEquals(2, count($hExpireResult3)); + $this->assertTrue(array_reduce($hExpireResult3, function($carry, $ttl) { return $carry && $ttl > 0; }, true)); // TTL should be positive + $this->assertEquals([-2], $hExpireResult4); + // REMOVE_END } protected function tearDown(): void diff --git a/local_examples/cmds_hash/redis-py/cmds_hash.py b/local_examples/cmds_hash/redis-py/cmds_hash.py index fd876b7195..434436e5d7 100644 --- a/local_examples/cmds_hash/redis-py/cmds_hash.py +++ b/local_examples/cmds_hash/redis-py/cmds_hash.py @@ -105,4 +105,28 @@ assert res11 == [ "Hello", "World" ] r.delete("myhash") # REMOVE_END +# STEP_END + +# STEP_START hexpire +# Set up hash with fields +r.hset("myhash", mapping={"field1": "Hello", "field2": "World"}) + +# Set expiration on hash fields +res12 = r.hexpire("myhash", 10, "field1", "field2") +print(res12) # >>> [1, 1] + +# Check TTL of the fields +res13 = r.httl("myhash", "field1", "field2") +print(res13) # >>> [10, 10] (or close to 10) + +# Try to set expiration on non-existent field +res14 = r.hexpire("myhash", 10, "nonexistent") +print(res14) # >>> [-2] + +# REMOVE_START +assert res12 == [1, 1] +assert all(ttl > 0 for ttl in res13) # TTL should be positive +assert res14 == [-2] +r.delete("myhash") +# REMOVE_END # STEP_END \ No newline at end of file diff --git a/local_examples/cmds_hash/rust-async/cmds_hash.rs b/local_examples/cmds_hash/rust-async/cmds_hash.rs index e9c1a86b80..58e10c5d8e 100644 --- a/local_examples/cmds_hash/rust-async/cmds_hash.rs +++ b/local_examples/cmds_hash/rust-async/cmds_hash.rs @@ -349,5 +349,64 @@ mod cmds_hash_tests { let _: Result = r.del("myhash").await; // REMOVE_END // STEP_END + + // STEP_START hexpire + // Set up hash with fields + let hash_fields = vec![("field1", "Hello"), ("field2", "World")]; + if let Ok(res) = r.hset_multiple("myhash", &hash_fields).await { + let res: String = res; + println!("{res}"); // >>> OK + } + + // Set expiration on hash fields + match r.hexpire("myhash", 10, redis::ExpireOption::NONE, &["field1", "field2"]).await { + Ok(res1) => { + let res1: Vec = res1; + println!("{:?}", res1); // >>> [1, 1] + // REMOVE_START + assert_eq!(res1, vec![1, 1]); + // REMOVE_END + }, + Err(e) => { + println!("Error setting expiration: {e}"); + return; + } + } + + // Check TTL of the fields + match r.httl("myhash", &["field1", "field2"]).await { + Ok(res2) => { + let res2: Vec = res2; + println!("{}", res2.len()); // >>> 2 + // REMOVE_START + assert_eq!(res2.len(), 2); + assert!(res2.iter().all(|&ttl| ttl > 0)); // TTL should be positive + // REMOVE_END + }, + Err(e) => { + println!("Error getting TTL: {e}"); + return; + } + } + + // Try to set expiration on non-existent field + match r.hexpire("myhash", 10, redis::ExpireOption::NONE, &["nonexistent"]).await { + Ok(res3) => { + let res3: Vec = res3; + println!("{:?}", res3); // >>> [-2] + // REMOVE_START + assert_eq!(res3, vec![-2]); + // REMOVE_END + }, + Err(e) => { + println!("Error setting expiration on non-existent field: {e}"); + return; + } + } + + // REMOVE_START + let _: Result = r.del("myhash").await; + // REMOVE_END + // STEP_END } } diff --git a/local_examples/cmds_hash/rust-sync/cmds_hash.rs b/local_examples/cmds_hash/rust-sync/cmds_hash.rs index 5ad056baee..cf6bd0ae10 100644 --- a/local_examples/cmds_hash/rust-sync/cmds_hash.rs +++ b/local_examples/cmds_hash/rust-sync/cmds_hash.rs @@ -349,5 +349,64 @@ mod cmds_hash_tests { let _: Result = r.del("myhash"); // REMOVE_END // STEP_END + + // STEP_START hexpire + // Set up hash with fields + let hash_fields = vec![("field1", "Hello"), ("field2", "World")]; + if let Ok(res) = r.hset_multiple("myhash", &hash_fields) { + let res: String = res; + println!("{res}"); // >>> OK + } + + // Set expiration on hash fields + match r.hexpire("myhash", 10, redis::ExpireOption::NONE, &["field1", "field2"]) { + Ok(res1) => { + let res1: Vec = res1; + println!("{:?}", res1); // >>> [1, 1] + // REMOVE_START + assert_eq!(res1, vec![1, 1]); + // REMOVE_END + }, + Err(e) => { + println!("Error setting expiration: {e}"); + return; + } + } + + // Check TTL of the fields + match r.httl("myhash", &["field1", "field2"]) { + Ok(res2) => { + let res2: Vec = res2; + println!("{}", res2.len()); // >>> 2 + // REMOVE_START + assert_eq!(res2.len(), 2); + assert!(res2.iter().all(|&ttl| ttl > 0)); // TTL should be positive + // REMOVE_END + }, + Err(e) => { + println!("Error getting TTL: {e}"); + return; + } + } + + // Try to set expiration on non-existent field + match r.hexpire("myhash", 10, redis::ExpireOption::NONE, &["nonexistent"]) { + Ok(res3) => { + let res3: Vec = res3; + println!("{:?}", res3); // >>> [-2] + // REMOVE_START + assert_eq!(res3, vec![-2]); + // REMOVE_END + }, + Err(e) => { + println!("Error setting expiration on non-existent field: {e}"); + return; + } + } + + // REMOVE_START + let _: Result = r.del("myhash"); + // REMOVE_END + // STEP_END } }