diff --git a/.github/workflows/integration.yaml b/.github/workflows/integration.yaml index f6186b8271..a619275b47 100644 --- a/.github/workflows/integration.yaml +++ b/.github/workflows/integration.yaml @@ -25,8 +25,8 @@ permissions: env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - REDIS_IMAGE: redis/redis-stack-server:7.4.0-rc1 - REDIS_STACK_IMAGE: redis/redis-stack-server:7.4.0-rc1 + REDIS_IMAGE: redis:7.4-rc2 + REDIS_STACK_IMAGE: redis/redis-stack-server:7.4.0-rc2 jobs: dependency-audit: diff --git a/redis/commands/core.py b/redis/commands/core.py index a56b3d2cba..26af9eb99c 100644 --- a/redis/commands/core.py +++ b/redis/commands/core.py @@ -5126,9 +5126,8 @@ def hexpire( lt: Set expiry only when the new expiry is less than the current one. Returns: - If the key does not exist, returns an empty list. If the key exists, returns - a list which contains for each field in the request: - - `-2` if the field does not exist. + Returns a list which contains for each field in the request: + - `-2` if the field does not exist, or if the key does not exist. - `0` if the specified NX | XX | GT | LT condition was not met. - `1` if the expiration time was set or updated. - `2` if the field was deleted because the specified expiration time is @@ -5187,9 +5186,8 @@ def hpexpire( lt: Set expiry only when the new expiry is less than the current one. Returns: - If the key does not exist, returns an empty list. If the key exists, returns - a list which contains for each field in the request: - - `-2` if the field does not exist. + Returns a list which contains for each field in the request: + - `-2` if the field does not exist, or if the key does not exist. - `0` if the specified NX | XX | GT | LT condition was not met. - `1` if the expiration time was set or updated. - `2` if the field was deleted because the specified expiration time is @@ -5248,9 +5246,8 @@ def hexpireat( lt: Set expiry only when the new expiry is less than the current one. Returns: - If the key does not exist, returns an empty list. If the key exists, returns - a list which contains for each field in the request: - - `-2` if the field does not exist. + Returns a list which contains for each field in the request: + - `-2` if the field does not exist, or if the key does not exist. - `0` if the specified NX | XX | GT | LT condition was not met. - `1` if the expiration time was set or updated. - `2` if the field was deleted because the specified expiration time is @@ -5315,9 +5312,8 @@ def hpexpireat( lt: Set expiry only when the new expiry is less than the current one. Returns: - If the key does not exist, returns an empty list. If the key exists, returns - a list which contains for each field in the request: - - `-2` if the field does not exist. + Returns a list which contains for each field in the request: + - `-2` if the field does not exist, or if the key does not exist. - `0` if the specified NX | XX | GT | LT condition was not met. - `1` if the expiration time was set or updated. - `2` if the field was deleted because the specified expiration time is @@ -5362,9 +5358,8 @@ def hpersist(self, name: KeyT, *fields: str) -> ResponseT: expiration time. Returns: - If the key does not exist, returns an empty list. If the key exists, returns - a list which contains for each field in the request: - - `-2` if the field does not exist. + Returns a list which contains for each field in the request: + - `-2` if the field does not exist, or if the key does not exist. - `-1` if the field exists but has no associated expiration time. - `1` if the expiration time was successfully removed from the field. """ @@ -5382,9 +5377,8 @@ def hexpiretime(self, key: KeyT, *fields: str) -> ResponseT: time. Returns: - If the key does not exist, returns an empty list. If the key exists, returns - a list which contains for each field in the request: - - `-2` if the field does not exist. + Returns a list which contains for each field in the request: + - `-2` if the field does not exist, or if the key does not exist. - `-1` if the field exists but has no associated expire time. - A positive integer representing the expiration Unix timestamp in seconds, if the field has an associated expiration time. @@ -5405,9 +5399,8 @@ def hpexpiretime(self, key: KeyT, *fields: str) -> ResponseT: time. Returns: - If the key does not exist, returns an empty list. If the key exists, returns - a list which contains for each field in the request: - - `-2` if the field does not exist. + Returns a list which contains for each field in the request: + - `-2` if the field does not exist, or if the key does not exist. - `-1` if the field exists but has no associated expire time. - A positive integer representing the expiration Unix timestamp in milliseconds, if the field has an associated expiration time. @@ -5428,9 +5421,8 @@ def httl(self, key: KeyT, *fields: str) -> ResponseT: fields: A list of fields within the hash for which to get the TTL. Returns: - If the key does not exist, returns an empty list. If the key exists, returns - a list which contains for each field in the request: - - `-2` if the field does not exist. + Returns a list which contains for each field in the request: + - `-2` if the field does not exist, or if the key does not exist. - `-1` if the field exists but has no associated expire time. - A positive integer representing the TTL in seconds if the field has an associated expiration time. @@ -5451,9 +5443,8 @@ def hpttl(self, key: KeyT, *fields: str) -> ResponseT: fields: A list of fields within the hash for which to get the TTL. Returns: - If the key does not exist, returns an empty list. If the key exists, returns - a list which contains for each field in the request: - - `-2` if the field does not exist. + Returns a list which contains for each field in the request: + - `-2` if the field does not exist, or if the key does not exist. - `-1` if the field exists but has no associated expire time. - A positive integer representing the TTL in milliseconds if the field has an associated expiration time. diff --git a/tests/test_asyncio/test_hash.py b/tests/test_asyncio/test_hash.py index d4f180539e..e31ea7eaf3 100644 --- a/tests/test_asyncio/test_hash.py +++ b/tests/test_asyncio/test_hash.py @@ -45,7 +45,7 @@ async def test_hexpire_conditions(r): @skip_if_server_version_lt("7.3.240") async def test_hexpire_nonexistent_key_or_field(r): await r.delete("test:hash") - assert await r.hexpire("test:hash", 1, "field1") == [] + assert await r.hexpire("test:hash", 1, "field1") == [-2] await r.hset("test:hash", "field1", "value1") assert await r.hexpire("test:hash", 1, "nonexistent_field") == [-2] @@ -105,7 +105,7 @@ async def test_hpexpire_conditions(r): @skip_if_server_version_lt("7.3.240") async def test_hpexpire_nonexistent_key_or_field(r): await r.delete("test:hash") - assert await r.hpexpire("test:hash", 500, "field1") == [] + assert await r.hpexpire("test:hash", 500, "field1") == [-2] await r.hset("test:hash", "field1", "value1") assert await r.hpexpire("test:hash", 500, "nonexistent_field") == [-2] @@ -163,7 +163,7 @@ async def test_hexpireat_conditions(r): async def test_hexpireat_nonexistent_key_or_field(r): await r.delete("test:hash") future_exp_time = int((datetime.now() + timedelta(seconds=1)).timestamp()) - assert await r.hexpireat("test:hash", future_exp_time, "field1") == [] + assert await r.hexpireat("test:hash", future_exp_time, "field1") == [-2] await r.hset("test:hash", "field1", "value1") assert await r.hexpireat("test:hash", future_exp_time, "nonexistent_field") == [-2] @@ -228,7 +228,7 @@ async def test_hpexpireat_nonexistent_key_or_field(r): future_exp_time = int( (datetime.now() + timedelta(milliseconds=500)).timestamp() * 1000 ) - assert await r.hpexpireat("test:hash", future_exp_time, "field1") == [] + assert await r.hpexpireat("test:hash", future_exp_time, "field1") == [-2] await r.hset("test:hash", "field1", "value1") assert await r.hpexpireat("test:hash", future_exp_time, "nonexistent_field") == [-2] diff --git a/tests/test_asyncio/test_json.py b/tests/test_asyncio/test_json.py index 507afc5621..852fd4aaa6 100644 --- a/tests/test_asyncio/test_json.py +++ b/tests/test_asyncio/test_json.py @@ -131,7 +131,7 @@ async def test_mset(decoded_r: redis.Redis): async def test_clear(decoded_r: redis.Redis): await decoded_r.json().set("arr", Path.root_path(), [0, 1, 2, 3, 4]) assert 1 == await decoded_r.json().clear("arr", Path.root_path()) - assert_resp_response(decoded_r, await decoded_r.json().get("arr"), [], [[[]]]) + assert_resp_response(decoded_r, await decoded_r.json().get("arr"), [], []) @pytest.mark.redismod diff --git a/tests/test_hash.py b/tests/test_hash.py index 7145b10a07..9ed5e98132 100644 --- a/tests/test_hash.py +++ b/tests/test_hash.py @@ -46,7 +46,7 @@ def test_hexpire_conditions(r): @skip_if_server_version_lt("7.3.240") def test_hexpire_nonexistent_key_or_field(r): r.delete("test:hash") - assert r.hexpire("test:hash", 1, "field1") == [] + assert r.hexpire("test:hash", 1, "field1") == [-2] r.hset("test:hash", "field1", "value1") assert r.hexpire("test:hash", 1, "nonexistent_field") == [-2] @@ -115,7 +115,7 @@ def test_hpexpire_conditions(r): @skip_if_server_version_lt("7.3.240") def test_hpexpire_nonexistent_key_or_field(r): r.delete("test:hash") - assert r.hpexpire("test:hash", 500, "field1") == [] + assert r.hpexpire("test:hash", 500, "field1") == [-2] r.hset("test:hash", "field1", "value1") assert r.hpexpire("test:hash", 500, "nonexistent_field") == [-2] @@ -182,7 +182,7 @@ def test_hexpireat_conditions(r): def test_hexpireat_nonexistent_key_or_field(r): r.delete("test:hash") future_exp_time = int((datetime.now() + timedelta(seconds=1)).timestamp()) - assert r.hexpireat("test:hash", future_exp_time, "field1") == [] + assert r.hexpireat("test:hash", future_exp_time, "field1") == [-2] r.hset("test:hash", "field1", "value1") assert r.hexpireat("test:hash", future_exp_time, "nonexistent_field") == [-2] @@ -257,7 +257,7 @@ def test_hpexpireat_nonexistent_key_or_field(r): future_exp_time = int( (datetime.now() + timedelta(milliseconds=500)).timestamp() * 1000 ) - assert r.hpexpireat("test:hash", future_exp_time, "field1") == [] + assert r.hpexpireat("test:hash", future_exp_time, "field1") == [-2] r.hset("test:hash", "field1", "value1") assert r.hpexpireat("test:hash", future_exp_time, "nonexistent_field") == [-2] @@ -298,7 +298,7 @@ def test_hpersist_multiple_fields(r): @skip_if_server_version_lt("7.3.240") def test_hpersist_nonexistent_key(r): r.delete("test:hash") - assert r.hpersist("test:hash", "field1", "field2", "field3") == [] + assert r.hpersist("test:hash", "field1", "field2", "field3") == [-2, -2, -2] @skip_if_server_version_lt("7.3.240") @@ -315,7 +315,7 @@ def test_hexpiretime_multiple_fields_mixed_conditions(r): @skip_if_server_version_lt("7.3.240") def test_hexpiretime_nonexistent_key(r): r.delete("test:hash") - assert r.hexpiretime("test:hash", "field1", "field2", "field3") == [] + assert r.hexpiretime("test:hash", "field1", "field2", "field3") == [-2, -2, -2] @skip_if_server_version_lt("7.3.240") @@ -332,7 +332,7 @@ def test_hpexpiretime_multiple_fields_mixed_conditions(r): @skip_if_server_version_lt("7.3.240") def test_hpexpiretime_nonexistent_key(r): r.delete("test:hash") - assert r.hpexpiretime("test:hash", "field1", "field2", "field3") == [] + assert r.hpexpiretime("test:hash", "field1", "field2", "field3") == [-2, -2, -2] @skip_if_server_version_lt("7.3.240") @@ -349,7 +349,7 @@ def test_httl_multiple_fields_mixed_conditions(r): @skip_if_server_version_lt("7.3.240") def test_httl_nonexistent_key(r): r.delete("test:hash") - assert r.httl("test:hash", "field1", "field2", "field3") == [] + assert r.httl("test:hash", "field1", "field2", "field3") == [-2, -2, -2] @skip_if_server_version_lt("7.3.240") @@ -366,4 +366,4 @@ def test_hpttl_multiple_fields_mixed_conditions(r): @skip_if_server_version_lt("7.3.240") def test_hpttl_nonexistent_key(r): r.delete("test:hash") - assert r.hpttl("test:hash", "field1", "field2", "field3") == [] + assert r.hpttl("test:hash", "field1", "field2", "field3") == [-2, -2, -2] diff --git a/tests/test_json.py b/tests/test_json.py index a688464874..50787c717c 100644 --- a/tests/test_json.py +++ b/tests/test_json.py @@ -130,7 +130,7 @@ def test_mset(client): def test_clear(client): client.json().set("arr", Path.root_path(), [0, 1, 2, 3, 4]) assert 1 == client.json().clear("arr", Path.root_path()) - assert_resp_response(client, client.json().get("arr"), [], [[[]]]) + assert_resp_response(client, client.json().get("arr"), [], []) @pytest.mark.redismod diff --git a/tests/test_search.py b/tests/test_search.py index 284bccd913..a61f361c3a 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -2369,27 +2369,27 @@ def test_search_missing_fields(client): with pytest.raises(redis.exceptions.ResponseError) as e: client.ft().search( - Query("ismissing(@title)").dialect(5).return_field("id").no_content() + Query("ismissing(@title)").dialect(2).return_field("id").no_content() ) assert "to be defined with 'INDEXMISSING'" in e.value.args[0] res = client.ft().search( - Query("ismissing(@features)").dialect(5).return_field("id").no_content() + Query("ismissing(@features)").dialect(2).return_field("id").no_content() ) _assert_search_result(client, res, ["property:2"]) res = client.ft().search( - Query("-ismissing(@features)").dialect(5).return_field("id").no_content() + Query("-ismissing(@features)").dialect(2).return_field("id").no_content() ) _assert_search_result(client, res, ["property:1", "property:3"]) res = client.ft().search( - Query("ismissing(@description)").dialect(5).return_field("id").no_content() + Query("ismissing(@description)").dialect(2).return_field("id").no_content() ) _assert_search_result(client, res, ["property:3"]) res = client.ft().search( - Query("-ismissing(@description)").dialect(5).return_field("id").no_content() + Query("-ismissing(@description)").dialect(2).return_field("id").no_content() ) _assert_search_result(client, res, ["property:1", "property:2"]) @@ -2438,27 +2438,29 @@ def test_search_empty_fields(client): with pytest.raises(redis.exceptions.ResponseError) as e: client.ft().search( - Query("@title:''").dialect(5).return_field("id").no_content() + Query("@title:''").dialect(2).return_field("id").no_content() ) - assert "to be defined with `INDEXEMPTY`" in e.value.args[0] + assert "Use `INDEXEMPTY` in field creation" in e.value.args[0] res = client.ft().search( - Query("@features:{ }").dialect(5).return_field("id").no_content() + Query("@features:{$empty}").dialect(2).return_field("id").no_content(), + query_params={"empty": ""}, ) _assert_search_result(client, res, ["property:2"]) res = client.ft().search( - Query("-@features:{ }").dialect(5).return_field("id").no_content() + Query("-@features:{$empty}").dialect(2).return_field("id").no_content(), + query_params={"empty": ""}, ) _assert_search_result(client, res, ["property:1", "property:3"]) res = client.ft().search( - Query("@description:''").dialect(5).return_field("id").no_content() + Query("@description:''").dialect(2).return_field("id").no_content() ) _assert_search_result(client, res, ["property:3"]) res = client.ft().search( - Query("-@description:''").dialect(5).return_field("id").no_content() + Query("-@description:''").dialect(2).return_field("id").no_content() ) _assert_search_result(client, res, ["property:1", "property:2"]) @@ -2503,14 +2505,13 @@ def test_special_characters_in_fields(client): ) _assert_search_result(client, res, ["resource:1"]) - # with dialect 5 no need to escape the - even without params + # with double quotes exact match no need to escape the - even without params res = client.ft().search( - Query("@uuid:{123e4567-e89b-12d3-a456-426614174000}").dialect(5) + Query('@uuid:{"123e4567-e89b-12d3-a456-426614174000"}').dialect(2) ) _assert_search_result(client, res, ["resource:1"]) - # also no need to escape ' with dialect 5 - res = client.ft().search(Query("@tags:{new-year's-resolutions}").dialect(5)) + res = client.ft().search(Query('@tags:{"new-year\'s-resolutions"}').dialect(2)) _assert_search_result(client, res, ["resource:2"]) # possible to search numeric fields by single value @@ -2518,7 +2519,7 @@ def test_special_characters_in_fields(client): _assert_search_result(client, res, ["resource:2"]) # some chars still need escaping - res = client.ft().search(Query(r"@tags:{\$btc}").dialect(5)) + res = client.ft().search(Query(r"@tags:{\$btc}").dialect(2)) _assert_search_result(client, res, ["resource:1"])