├── .env.example ├── .github └── workflows │ ├── release.yaml │ └── test.yaml ├── .gitignore ├── LICENSE ├── README.md ├── examples ├── chat_history.py ├── scan_queue.py ├── simple.py ├── simple_async.py └── twitter_clone.py ├── pyproject.toml ├── tests ├── __init__.py ├── commands │ ├── asyncio │ │ ├── bitmap │ │ │ ├── test_bitcount.py │ │ │ ├── test_bitfield.py │ │ │ ├── test_bitfield_ro.py │ │ │ ├── test_bitop.py │ │ │ ├── test_bitpos.py │ │ │ ├── test_getbit.py │ │ │ └── test_setbit.py │ │ ├── connection │ │ │ ├── test_echo.py │ │ │ └── test_ping.py │ │ ├── generic │ │ │ ├── test_copy.py │ │ │ ├── test_delete.py │ │ │ ├── test_exists.py │ │ │ ├── test_expire.py │ │ │ ├── test_expireat.py │ │ │ ├── test_keys.py │ │ │ ├── test_persist.py │ │ │ ├── test_pexpire.py │ │ │ ├── test_pexpireat.py │ │ │ ├── test_pttl.py │ │ │ ├── test_randomkey.py │ │ │ ├── test_rename.py │ │ │ ├── test_renamenx.py │ │ │ ├── test_scan.py │ │ │ ├── test_touch.py │ │ │ ├── test_ttl.py │ │ │ ├── test_type.py │ │ │ └── test_unlink.py │ │ ├── geo │ │ │ ├── test_geoadd.py │ │ │ ├── test_geodist.py │ │ │ ├── test_geohash.py │ │ │ ├── test_geopos.py │ │ │ ├── test_georadius.py │ │ │ ├── test_georadius_ro.py │ │ │ ├── test_georadiusbymember.py │ │ │ ├── test_georadiusbymember_ro.py │ │ │ ├── test_geosearch_georadius.py │ │ │ ├── test_geosearch_georadiusbymember.py │ │ │ ├── test_geosearchstore_georadius.py │ │ │ └── test_geosearchstore_georadiusbymember.py │ │ ├── hyperloglog │ │ │ ├── test_pfadd.py │ │ │ ├── test_pfcount.py │ │ │ └── test_pfmerge.py │ │ ├── pubsub │ │ │ └── test_publish.py │ │ ├── scripting │ │ │ ├── test_eval.py │ │ │ ├── test_eval_ro.py │ │ │ ├── test_evalsha.py │ │ │ ├── test_evalsha_ro.py │ │ │ ├── test_script_exists.py │ │ │ ├── test_script_flush.py │ │ │ └── test_script_load.py │ │ └── test_asyncio_pipeline.py │ ├── hash │ │ ├── test_hdel.py │ │ ├── test_hexists.py │ │ ├── test_hexpire.py │ │ ├── test_hexpireat.py │ │ ├── test_hexpiretime.py │ │ ├── test_hget.py │ │ ├── test_hgetall.py │ │ ├── test_hincrby.py │ │ ├── test_hincrbyfloat.py │ │ ├── test_hkeys.py │ │ ├── test_hlen.py │ │ ├── test_hmget.py │ │ ├── test_hmset.py │ │ ├── test_hpersist.py │ │ ├── test_hpexpire.py │ │ ├── test_hpexpireat.py │ │ ├── test_hpexpiretime.py │ │ ├── test_hpttl.py │ │ ├── test_hrandfield.py │ │ ├── test_hscan.py │ │ ├── test_hset.py │ │ ├── test_hsetnx.py │ │ ├── test_hstrlen.py │ │ ├── test_httl.py │ │ └── test_hvals.py │ ├── json │ │ ├── test_json_arrappend.py │ │ ├── test_json_arrindex.py │ │ ├── test_json_arrinsert.py │ │ ├── test_json_arrlen.py │ │ ├── test_json_arrpop.py │ │ ├── test_json_arrtrim.py │ │ ├── test_json_clear.py │ │ ├── test_json_delete.py │ │ ├── test_json_forget.py │ │ ├── test_json_get.py │ │ ├── test_json_merge.py │ │ ├── test_json_mget.py │ │ ├── test_json_mset.py │ │ ├── test_json_numincrby.py │ │ ├── test_json_nummultby.py │ │ ├── test_json_objkeys.py │ │ ├── test_json_objlen.py │ │ ├── test_json_resp.py │ │ ├── test_json_set.py │ │ ├── test_json_strappend.py │ │ ├── test_json_strlen.py │ │ ├── test_json_toggle.py │ │ └── test_json_type.py │ ├── list │ │ ├── test_lindex.py │ │ ├── test_linsert.py │ │ ├── test_llen.py │ │ ├── test_lmove.py │ │ ├── test_lpop.py │ │ ├── test_lpos.py │ │ ├── test_lpush.py │ │ ├── test_lpushx.py │ │ ├── test_lrange.py │ │ ├── test_lrem.py │ │ ├── test_lset.py │ │ ├── test_ltrim.py │ │ ├── test_rpop.py │ │ ├── test_rpoplpush.py │ │ ├── test_rpush.py │ │ └── test_rpushx.py │ ├── server │ │ ├── test_dbsize.py │ │ ├── test_flushall.py │ │ ├── test_flushdb.py │ │ └── test_time.py │ ├── set │ │ ├── test_sadd.py │ │ ├── test_scard.py │ │ ├── test_sdiff.py │ │ ├── test_sdiffstore.py │ │ ├── test_sinter.py │ │ ├── test_sinterstore.py │ │ ├── test_sismember.py │ │ ├── test_smembers.py │ │ ├── test_smismember.py │ │ ├── test_smove.py │ │ ├── test_spop.py │ │ ├── test_srandmember.py │ │ ├── test_srem.py │ │ ├── test_sscan.py │ │ ├── test_sunion.py │ │ └── test_sunionstore.py │ ├── sortedSet │ │ ├── test_zadd.py │ │ ├── test_zcard.py │ │ ├── test_zcount.py │ │ ├── test_zdiff.py │ │ ├── test_zdiffstore.py │ │ ├── test_zincrby.py │ │ ├── test_zinter.py │ │ ├── test_zinterstore.py │ │ ├── test_zlexcount.py │ │ ├── test_zmscore.py │ │ ├── test_zpopmax.py │ │ ├── test_zpopmin.py │ │ ├── test_zrandmember.py │ │ ├── test_zrange.py │ │ ├── test_zrangebylex.py │ │ ├── test_zrangebyscore.py │ │ ├── test_zrangestore.py │ │ ├── test_zrank.py │ │ ├── test_zrem.py │ │ ├── test_zremrangebylex.py │ │ ├── test_zremrangebyrank.py │ │ ├── test_zremrangebyscore.py │ │ ├── test_zrevrange.py │ │ ├── test_zrevrangebylex.py │ │ ├── test_zrevrangebyscore.py │ │ ├── test_zrevrank.py │ │ ├── test_zscan.py │ │ ├── test_zscore.py │ │ ├── test_zunion.py │ │ └── test_zunionstore.py │ ├── string │ │ ├── test_append.py │ │ ├── test_decr.py │ │ ├── test_decrby.py │ │ ├── test_get.py │ │ ├── test_getdel.py │ │ ├── test_getex.py │ │ ├── test_getrange.py │ │ ├── test_getset.py │ │ ├── test_incr.py │ │ ├── test_incrby.py │ │ ├── test_incrbyfloat.py │ │ ├── test_mget.py │ │ ├── test_mset.py │ │ ├── test_msetnx.py │ │ ├── test_psetex.py │ │ ├── test_set.py │ │ ├── test_setex.py │ │ ├── test_setnx.py │ │ ├── test_setrange.py │ │ └── test_strlen.py │ └── test_pipeline.py ├── conftest.py ├── execute_on_http.py ├── test_client.py ├── test_execute.py ├── test_formatters.py ├── test_http.py ├── test_read_your_writes.py └── test_utils.py └── upstash_redis ├── __init__.py ├── asyncio ├── __init__.py └── client.py ├── client.py ├── commands.py ├── commands.pyi ├── errors.py ├── format.py ├── http.py ├── py.typed ├── typing.py └── utils.py /.env.example: -------------------------------------------------------------------------------- 1 | UPSTASH_REDIS_REST_URL="" 2 | UPSTASH_REDIS_REST_TOKEN="" 3 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: workflow_dispatch 4 | 5 | jobs: 6 | release: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - name: Checkout repository 11 | uses: actions/checkout@v2 12 | 13 | - name: Set up Python 14 | uses: actions/setup-python@v2 15 | with: 16 | python-version: 3.8 17 | 18 | - name: Install Poetry 19 | run: curl -sSL https://install.python-poetry.org | python3 - --version 1.8.4 20 | 21 | - name: Build and publish 22 | run: | 23 | poetry config pypi-token.pypi "${{secrets.PYPI_TOKEN}}" 24 | poetry build 25 | poetry publish --no-interaction 26 | 27 | - name: Generate release tag 28 | run: echo "RELEASE_TAG=v$(poetry version | awk '{print $2}')" >> $GITHUB_ENV 29 | 30 | - name: Create GitHub Release 31 | uses: actions/create-release@v1 32 | env: 33 | GITHUB_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }} 34 | with: 35 | tag_name: ${{ env.RELEASE_TAG }} 36 | release_name: Release ${{ env.RELEASE_TAG }} 37 | draft: false 38 | prerelease: false -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | test: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout repository 14 | uses: actions/checkout@v2 15 | 16 | - name: Set up Python 17 | uses: actions/setup-python@v2 18 | with: 19 | python-version: 3.8 20 | 21 | - name: Install Poetry 22 | run: curl -sSL https://install.python-poetry.org | python3 - --version 1.8.4 23 | 24 | - name: Set up Poetry environment 25 | run: poetry install --no-root 26 | 27 | - name: Run ruff 28 | run: | 29 | poetry run ruff check . 30 | 31 | - name: Run ruff format 32 | run: | 33 | poetry run ruff format --check . 34 | 35 | - name: Run mypy 36 | run: | 37 | poetry run mypy --show-error-codes . 38 | 39 | - name: Run tests 40 | run: | 41 | export UPSTASH_REDIS_REST_URL="${{secrets.UPSTASH_REDIS_REST_URL}}" 42 | export UPSTASH_REDIS_REST_TOKEN="${{secrets.UPSTASH_REDIS_REST_TOKEN}}" 43 | poetry run pytest 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | # PyCharm project settings 132 | .idea 133 | 134 | # VS Code settings 135 | .vscode 136 | 137 | # Poetry lock 138 | poetry.lock 139 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Upstash, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/chat_history.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | from upstash_redis import Redis 4 | 5 | # An example chat history model for storing 6 | # messages of different users with the chatbot 7 | 8 | # A user can have multiple chats 9 | # A chat can have multiple messages 10 | 11 | # user:123:chats -> set of chat ids 12 | 13 | # chat:123 -> list of messages 14 | 15 | # chat:next_id -> 1 (incrementing id for chats) 16 | 17 | 18 | class ChatModel: 19 | def __init__(self, redis: Redis): 20 | self.redis = redis 21 | 22 | def get_user_chats(self, user_id: str) -> List[str]: 23 | # A set of chat ids for a user, stores which chats belong to the user 24 | return self.redis.smembers(f"user:{user_id}:chats") 25 | 26 | def get_chat_messages(self, chat_id: str) -> List[str]: 27 | return self.redis.lrange(f"chat:{chat_id}", 0, -1) 28 | 29 | def add_message(self, chat_id: str, message: str): 30 | # Push the message to the end of the list 31 | self.redis.rpush(f"chat:{chat_id}", message) 32 | 33 | def create_chat(self, user_id: str) -> str: 34 | # A unique incrementing id for the chat 35 | # Since increment is atomic and returns the new value 36 | chat_id = str(self.redis.incr("chat:next_id")) 37 | 38 | # Add the chat to the user's chat list 39 | self.redis.sadd(f"user:{user_id}:chats", chat_id) 40 | 41 | return chat_id 42 | 43 | def delete_chat(self, chat_id: str, user_id: str): 44 | # Remove the chat from the user's chat list 45 | self.redis.srem(f"user:{user_id}:chats", str(chat_id)) 46 | 47 | # Delete the chat 48 | self.redis.delete(f"chat:{chat_id}") 49 | 50 | def delete_user(self, user_id: str): 51 | # Delete all chats of the user 52 | for chat_id in self.get_user_chats(user_id): 53 | self.delete_chat(chat_id, user_id) 54 | 55 | # Delete the user's chat list 56 | self.redis.delete(f"user:{user_id}:chats") 57 | 58 | 59 | redis = Redis.from_env() 60 | chat = ChatModel(redis) 61 | 62 | chat.redis.flushall() 63 | 64 | # You can acquire the userid from an authentication service 65 | # or from the session cookie 66 | userid = "myid" 67 | 68 | chatid_1 = chat.create_chat(userid) 69 | 70 | chat.add_message(chatid_1, "user:Hello") 71 | 72 | chat.add_message(chatid_1, "bot:Hello") 73 | 74 | chat.add_message(chatid_1, "user:How are you?") 75 | 76 | chatid_2 = chat.create_chat(userid) 77 | 78 | chat.add_message(chatid_2, "user:This is chat2") 79 | 80 | chat_ids = chat.get_user_chats(userid) 81 | 82 | # Print all the data 83 | print("chatid_1:", chatid_1) 84 | print("chatid_2:", chatid_2) 85 | print("chat_ids:", chat_ids) 86 | 87 | print("chat 1 messages:", chat.get_chat_messages(chatid_1)) 88 | print("chat 2 messages:", chat.get_chat_messages(chatid_2)) 89 | 90 | # Delete the first chat 91 | chat.delete_user(userid) 92 | 93 | print("chatids after deletion:", chat.get_user_chats(userid)) 94 | 95 | # Output 96 | # chatid_1: 1 97 | # chatid_2: 2 98 | # chat_ids: ['2', '1'] 99 | # chat 1 messages: ['user:Hello', 'bot:Hello', 'user:How are you?'] 100 | # chat 2 messages: ['user:This is chat2'] 101 | # chatids after deletion: [] 102 | -------------------------------------------------------------------------------- /examples/scan_queue.py: -------------------------------------------------------------------------------- 1 | from threading import Thread 2 | from typing import List, Optional, Tuple 3 | 4 | from upstash_redis import Redis 5 | 6 | # A simple task queue for scanning websites 7 | 8 | # Multiple workers can run the same code and 9 | # pop tasks from the queue 10 | 11 | # Scan the website and store the results in list 12 | 13 | # scan:waiting -> list of tasks with 14 | 15 | # scan:running -> a set of running tasks, 16 | # since a task must be popped from the waiting list by its id, 17 | # we use a set 18 | 19 | 20 | # scan:completed:123 -> a list of completed tasks for the client 21 | class TaskQueue: 22 | def __init__(self, redis: Redis): 23 | self.redis = redis 24 | 25 | def add_scan_task(self, clientid: str, website: str): 26 | # Add the task to the waiting list 27 | self.redis.rpush("scan:waiting", f"{clientid}:{website}") 28 | 29 | def consume_completed_tasks(self, clientid: str) -> List[str]: 30 | # Consume all completed tasks for the client 31 | # and return them 32 | 33 | # Redis doesn't have a popall command but you can atomically 34 | # pop all elements from a list by specifying a large count 35 | 36 | # Ignore type since if count is specified, this method returns a list 37 | return self.redis.rpop(f"scan:completed:{clientid}", 99999) # type: ignore 38 | 39 | def start_scan(self) -> Optional[Tuple[str, str]]: 40 | # Pop task from the waiting list and add it to the running list 41 | task: str | None = self.redis.rpop("scan:waiting") # type: ignore 42 | 43 | if task is None: 44 | # No task to run 45 | return None 46 | 47 | self.redis.sadd("scan:running", task) 48 | 49 | [client, website] = task.split(":", 1) 50 | 51 | return (client, website) 52 | 53 | def complete_scan(self, clientid: str, website: str, result: str): 54 | # Add the result to the client's completed list 55 | self.redis.rpush(f"scan:completed:{clientid}", result) 56 | 57 | # Remove the task from the running list 58 | self.redis.srem("scan:running", f"{clientid}:{website}") 59 | 60 | 61 | redis = Redis.from_env() 62 | queue = TaskQueue(redis) 63 | 64 | queue.add_scan_task("client1", "https://example.com") 65 | 66 | queue.add_scan_task("client1", "https://google.com") 67 | 68 | 69 | # This code will be run by multiple workers 70 | def work(): 71 | task = queue.start_scan() 72 | 73 | if task is None: 74 | return 75 | 76 | [client, website] = task 77 | 78 | # scan the website and store the result in a list 79 | queue.complete_scan(client, website, f"results for {website}") 80 | 81 | 82 | t1 = Thread(target=work) 83 | t2 = Thread(target=work) 84 | 85 | t1.start() 86 | t2.start() 87 | 88 | t1.join() 89 | t2.join() 90 | 91 | completed = queue.consume_completed_tasks("client1") 92 | 93 | print("completed: ", completed) 94 | -------------------------------------------------------------------------------- /examples/simple.py: -------------------------------------------------------------------------------- 1 | from upstash_redis import Redis 2 | 3 | # Reads from the environment variables 4 | # UPSTASH_REDIS_REST_URL 5 | # UPSTASH_REDIS_REST_TOKEN 6 | redis = Redis.from_env() 7 | 8 | # Set or get a key 9 | redis.set("key", "value") 10 | 11 | # 10 is converted to "10", it's still a string 12 | redis.set("key", 10) 13 | 14 | # Expires in 10 seconds 15 | redis.set("expire_key", value="expire_value", ex=10) 16 | 17 | # Gets the time to live in seconds 18 | redis.ttl("expire_key") 19 | 20 | # Change ttl 21 | redis.expire("expire_key", 20) 22 | 23 | # Remove ttl 24 | redis.persist("expire_key") 25 | 26 | # Set a key only if it does not exist 27 | redis.set("key", "value", nx=True) 28 | 29 | # Set a key only if it exists 30 | redis.set("key", "value", xx=True) 31 | -------------------------------------------------------------------------------- /examples/simple_async.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | from upstash_redis.asyncio import Redis 4 | 5 | 6 | async def main(): 7 | # Reads from the environment variables 8 | # UPSTASH_REDIS_REST_URL 9 | # UPSTASH_REDIS_REST_TOKEN 10 | redis = Redis.from_env() 11 | 12 | # Set or get a key 13 | await redis.set("key", "value") 14 | 15 | # 10 is converted to "10", it's still a string 16 | await redis.set("key", 10) 17 | 18 | # Expires in 10 seconds 19 | await redis.set("expire_key", value="expire_value", ex=10) 20 | 21 | # Gets the time to live in seconds 22 | await redis.ttl("expire_key") 23 | 24 | # Change ttl 25 | await redis.expire("expire_key", 20) 26 | 27 | # Remove ttl 28 | await redis.persist("expire_key") 29 | 30 | # Set a key only if it does not exist 31 | await redis.set("key", "value", nx=True) 32 | 33 | # Set a key only if it exists 34 | await redis.set("key", "value", xx=True) 35 | 36 | 37 | asyncio.run(main()) 38 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "upstash-redis" 3 | version = "1.4.0" 4 | description = "Serverless Redis SDK from Upstash" 5 | license = "MIT" 6 | authors = ["Upstash ", "Zgîmbău Tudor "] 7 | maintainers = ["Upstash "] 8 | readme = "README.md" 9 | repository = "https://github.com/upstash/redis-python" 10 | keywords = ["Upstash Redis", "Serverless Redis"] 11 | classifiers = [ 12 | "Development Status :: 5 - Production/Stable", 13 | "Intended Audience :: Developers", 14 | "License :: OSI Approved :: MIT License", 15 | "Operating System :: OS Independent", 16 | "Programming Language :: Python", 17 | "Programming Language :: Python :: 3", 18 | "Programming Language :: Python :: 3 :: Only", 19 | "Programming Language :: Python :: 3.8", 20 | "Programming Language :: Python :: 3.9", 21 | "Programming Language :: Python :: 3.10", 22 | "Programming Language :: Python :: 3.11", 23 | "Programming Language :: Python :: 3.12", 24 | "Programming Language :: Python :: 3.13", 25 | "Programming Language :: Python :: Implementation :: CPython", 26 | "Topic :: Database", 27 | "Topic :: Database :: Front-Ends", 28 | "Topic :: Software Development :: Libraries", 29 | ] 30 | packages = [{ include = "upstash_redis" }] 31 | 32 | [tool.poetry.dependencies] 33 | python = "^3.8" 34 | httpx = ">=0.23.0, <1" 35 | 36 | [tool.poetry.group.dev.dependencies] 37 | pytest = "^8.3.4" 38 | pytest-asyncio = "^0.24.0" 39 | python-dotenv = "^1.0.1" 40 | mypy = "^1.14.1" 41 | ruff = "^0.9.7" 42 | 43 | [build-system] 44 | requires = ["poetry-core"] 45 | build-backend = "poetry.core.masonry.api" 46 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/upstash/redis-py/38d27f60c0219a427e4471781eaf8daeedb642a7/tests/__init__.py -------------------------------------------------------------------------------- /tests/commands/asyncio/bitmap/test_bitcount.py: -------------------------------------------------------------------------------- 1 | from pytest import mark, raises 2 | 3 | from upstash_redis.asyncio import Redis 4 | 5 | 6 | @mark.asyncio 7 | async def test(async_redis: Redis) -> None: 8 | assert await async_redis.bitcount("string") == 17 9 | 10 | 11 | @mark.asyncio 12 | async def test_with_interval(async_redis: Redis) -> None: 13 | assert await async_redis.bitcount("string", start=1, end=2) == 9 14 | 15 | 16 | @mark.asyncio 17 | async def test_with_invalid_interval(async_redis: Redis) -> None: 18 | with raises(Exception) as exception: 19 | await async_redis.bitcount("string", end=2) 20 | 21 | assert str(exception.value) == 'Both "start" and "end" must be specified.' 22 | -------------------------------------------------------------------------------- /tests/commands/asyncio/bitmap/test_bitfield.py: -------------------------------------------------------------------------------- 1 | from pytest import mark 2 | 3 | from tests.execute_on_http import execute_on_http 4 | from upstash_redis.asyncio import Redis 5 | 6 | 7 | @mark.asyncio 8 | async def test_get(async_redis: Redis) -> None: 9 | # With integer offset. 10 | assert await async_redis.bitfield("string").get( 11 | encoding="u8", offset=0 12 | ).execute() == [116] 13 | 14 | # With string offset. 15 | assert await async_redis.bitfield("string").get( 16 | encoding="u8", offset="#1" 17 | ).execute() == [101] 18 | 19 | 20 | @mark.asyncio 21 | async def test_set(async_redis: Redis) -> None: 22 | # With integer offset. 23 | assert await async_redis.bitfield("string_for_bitfield_set").set( 24 | encoding="u8", offset=0, value=97 25 | ).execute() == [116] 26 | 27 | assert await execute_on_http( 28 | "BITFIELD", "string_for_bitfield_set", "GET", "u8", "0" 29 | ) == [97] 30 | 31 | # With string offset. 32 | assert await async_redis.bitfield("string_for_bitfield_set").set( 33 | encoding="u8", offset="#1", value=115 34 | ).execute() == [101] 35 | 36 | assert await execute_on_http( 37 | "BITFIELD", "string_for_bitfield_set", "GET", "u8", "#1" 38 | ) == [115] 39 | 40 | 41 | @mark.asyncio 42 | async def test_incrby(async_redis: Redis) -> None: 43 | # With integer offset. 44 | assert await async_redis.bitfield("string_for_bitfield_incrby").incrby( 45 | encoding="u8", offset=0, increment=1 46 | ).execute() == [117] 47 | 48 | assert await execute_on_http( 49 | "BITFIELD", "string_for_bitfield_incrby", "GET", "u8", "0" 50 | ) == [117] 51 | 52 | # With string offset. 53 | assert await async_redis.bitfield("string_for_bitfield_incrby").incrby( 54 | encoding="u8", offset="#1", increment=2 55 | ).execute() == [103] 56 | 57 | assert await execute_on_http( 58 | "BITFIELD", "string_for_bitfield_incrby", "GET", "u8", "#1" 59 | ) == [103] 60 | 61 | 62 | @mark.asyncio 63 | async def test_chained_commands(async_redis: Redis) -> None: 64 | assert await async_redis.bitfield("string_for_bitfield_chained_commands").set( 65 | encoding="u8", offset=0, value=97 66 | ).incrby(encoding="u8", offset=0, increment=1).execute() == [116, 98] 67 | 68 | assert await execute_on_http( 69 | "BITFIELD", "string_for_bitfield_chained_commands", "GET", "u8", "0" 70 | ) == [98] 71 | 72 | 73 | @mark.asyncio 74 | async def test_overflow(async_redis: Redis) -> None: 75 | assert await async_redis.bitfield("string_for_bitfield_overflow").incrby( 76 | encoding="i8", offset=100, increment=100 77 | ).overflow("SAT").incrby(encoding="i8", offset=100, increment=100).execute() == [ 78 | 100, 79 | 127, 80 | ] 81 | 82 | assert await execute_on_http( 83 | "BITFIELD", "string_for_bitfield_overflow", "GET", "i8", "100" 84 | ) == [127] 85 | -------------------------------------------------------------------------------- /tests/commands/asyncio/bitmap/test_bitfield_ro.py: -------------------------------------------------------------------------------- 1 | from pytest import mark 2 | 3 | from upstash_redis.asyncio import Redis 4 | 5 | 6 | @mark.asyncio 7 | async def test_get(async_redis: Redis) -> None: 8 | # With integer offset. 9 | assert await async_redis.bitfield_ro("string").get( 10 | encoding="u8", offset=0 11 | ).execute() == [116] 12 | 13 | # With string offset. 14 | assert await async_redis.bitfield_ro("string").get( 15 | encoding="u8", offset="#1" 16 | ).execute() == [101] 17 | 18 | 19 | @mark.asyncio 20 | async def test_chained_commands(async_redis: Redis) -> None: 21 | assert await async_redis.bitfield_ro("string").get(encoding="u8", offset=0).get( 22 | encoding="u8", offset="#1" 23 | ).execute() == [116, 101] 24 | -------------------------------------------------------------------------------- /tests/commands/asyncio/bitmap/test_bitop.py: -------------------------------------------------------------------------------- 1 | from pytest import mark, raises 2 | 3 | from tests.execute_on_http import execute_on_http 4 | from upstash_redis.asyncio import Redis 5 | 6 | 7 | @mark.asyncio 8 | async def test_not_not_operation(async_redis: Redis) -> None: 9 | assert ( 10 | await async_redis.bitop( 11 | "AND", 12 | "bitop_destination_1", 13 | "string_as_bitop_source_1", 14 | "string_as_bitop_source_2", 15 | ) 16 | == 4 17 | ) 18 | 19 | assert await execute_on_http("GET", "bitop_destination_1") == '!"#$' 20 | 21 | 22 | @mark.asyncio 23 | async def test_without_source_keys(async_redis: Redis) -> None: 24 | with raises(Exception) as exception: 25 | await async_redis.bitop("AND", "bitop_destination_1") 26 | 27 | assert str(exception.value) == "At least one source key must be specified." 28 | 29 | 30 | @mark.asyncio 31 | async def test_not_with_more_than_one_source_key(async_redis: Redis) -> None: 32 | with raises(Exception) as exception: 33 | await async_redis.bitop( 34 | "NOT", 35 | "bitop_destination_4", 36 | "string_as_bitop_source_1", 37 | "string_as_bitop_source_2", 38 | ) 39 | 40 | assert ( 41 | str(exception.value) 42 | == 'The "NOT " operation takes only one source key as argument.' 43 | ) 44 | 45 | 46 | @mark.asyncio 47 | async def test_not(async_redis: Redis) -> None: 48 | assert ( 49 | await async_redis.bitop( 50 | "NOT", "bitop_destination_4", "string_as_bitop_source_1" 51 | ) 52 | == 4 53 | ) 54 | -------------------------------------------------------------------------------- /tests/commands/asyncio/bitmap/test_bitpos.py: -------------------------------------------------------------------------------- 1 | from pytest import mark, raises 2 | 3 | from upstash_redis.asyncio import Redis 4 | 5 | 6 | @mark.asyncio 7 | async def test(async_redis: Redis) -> None: 8 | assert await async_redis.bitpos("string", bit=1) == 1 9 | 10 | 11 | @mark.asyncio 12 | async def test_with_interval(async_redis: Redis) -> None: 13 | assert await async_redis.bitpos("string", bit=0, start=1, end=0) == -1 14 | 15 | assert await async_redis.bitpos("string", bit=0, start=1) == 8 16 | 17 | 18 | @mark.asyncio 19 | async def test_with_start_and_not_end(async_redis: Redis) -> None: 20 | with raises(Exception) as exception: 21 | await async_redis.bitpos("string", bit=0, end=2) 22 | 23 | assert str(exception.value) == '"end" is specified, but "start" is missing.' 24 | -------------------------------------------------------------------------------- /tests/commands/asyncio/bitmap/test_getbit.py: -------------------------------------------------------------------------------- 1 | from pytest import mark 2 | 3 | from upstash_redis.asyncio import Redis 4 | 5 | 6 | @mark.asyncio 7 | async def test(async_redis: Redis) -> None: 8 | assert await async_redis.getbit(key="string", offset=1) == 1 9 | -------------------------------------------------------------------------------- /tests/commands/asyncio/bitmap/test_setbit.py: -------------------------------------------------------------------------------- 1 | from pytest import mark 2 | 3 | from tests.execute_on_http import execute_on_http 4 | from upstash_redis.asyncio import Redis 5 | 6 | 7 | @mark.asyncio 8 | async def test(async_redis: Redis) -> None: 9 | assert await async_redis.setbit("setbit", offset=4, value=1) == 0 10 | 11 | assert await execute_on_http("GETBIT", "setbit", "4") == 1 12 | -------------------------------------------------------------------------------- /tests/commands/asyncio/connection/test_echo.py: -------------------------------------------------------------------------------- 1 | from pytest import mark 2 | 3 | from upstash_redis.asyncio import Redis 4 | 5 | 6 | @mark.asyncio 7 | async def test(async_redis: Redis) -> None: 8 | assert await async_redis.echo(message="Upstash is nice!") == "Upstash is nice!" 9 | -------------------------------------------------------------------------------- /tests/commands/asyncio/connection/test_ping.py: -------------------------------------------------------------------------------- 1 | from pytest import mark 2 | 3 | from upstash_redis.asyncio import Redis 4 | 5 | 6 | @mark.asyncio 7 | async def test(async_redis: Redis) -> None: 8 | assert await async_redis.ping() == "PONG" 9 | 10 | 11 | @mark.asyncio 12 | async def test_with_message(async_redis: Redis) -> None: 13 | assert await async_redis.ping(message="Upstash is nice!") == "Upstash is nice!" 14 | -------------------------------------------------------------------------------- /tests/commands/asyncio/generic/test_copy.py: -------------------------------------------------------------------------------- 1 | from pytest import mark 2 | 3 | from tests.execute_on_http import execute_on_http 4 | from upstash_redis.asyncio import Redis 5 | 6 | 7 | @mark.asyncio 8 | async def test(async_redis: Redis) -> None: 9 | assert ( 10 | await async_redis.copy(source="string", destination="copy_destination") is True 11 | ) 12 | 13 | assert await execute_on_http("GET", "copy_destination") == "test" 14 | 15 | 16 | @mark.asyncio 17 | async def test_with_replace(async_redis: Redis) -> None: 18 | assert ( 19 | await async_redis.copy( 20 | source="string", destination="string_as_copy_destination", replace=True 21 | ) 22 | is True 23 | ) 24 | 25 | assert await execute_on_http("GET", "string_as_copy_destination") == "test" 26 | 27 | 28 | @mark.asyncio 29 | async def test_with_formatting(async_redis: Redis) -> None: 30 | await async_redis.copy(source="string", destination="copy_destination_2") 31 | assert ( 32 | await async_redis.copy(source="string", destination="copy_destination_2") 33 | is False 34 | ) 35 | -------------------------------------------------------------------------------- /tests/commands/asyncio/generic/test_delete.py: -------------------------------------------------------------------------------- 1 | from pytest import mark, raises 2 | 3 | from tests.execute_on_http import execute_on_http 4 | from upstash_redis.asyncio import Redis 5 | 6 | 7 | @mark.asyncio 8 | async def test(async_redis: Redis) -> None: 9 | assert await async_redis.delete("string_for_delete_1", "string_for_delete_2") == 2 10 | 11 | assert ( 12 | await execute_on_http("EXISTS", "string_for_delete_1", "string_for_delete_2") 13 | == 0 14 | ) 15 | 16 | 17 | @mark.asyncio 18 | async def test_without_keys(async_redis: Redis) -> None: 19 | with raises(Exception) as exception: 20 | await async_redis.delete() 21 | 22 | assert str(exception.value) == "At least one key must be deleted." 23 | -------------------------------------------------------------------------------- /tests/commands/asyncio/generic/test_exists.py: -------------------------------------------------------------------------------- 1 | from pytest import mark, raises 2 | 3 | from upstash_redis.asyncio import Redis 4 | 5 | 6 | @mark.asyncio 7 | async def test(async_redis: Redis) -> None: 8 | assert await async_redis.exists("string", "hash") == 2 9 | 10 | 11 | @mark.asyncio 12 | async def test_without_keys(async_redis: Redis) -> None: 13 | with raises(Exception) as exception: 14 | await async_redis.exists() 15 | 16 | assert str(exception.value) == "At least one key must be checked." 17 | -------------------------------------------------------------------------------- /tests/commands/asyncio/generic/test_expire.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | from asyncio import sleep 3 | 4 | from pytest import mark 5 | 6 | from tests.execute_on_http import execute_on_http 7 | from upstash_redis.asyncio import Redis 8 | 9 | 10 | @mark.asyncio 11 | async def test(async_redis: Redis) -> None: 12 | assert await async_redis.expire("string_for_expire", seconds=1) is True 13 | 14 | # Check if the expiry was correctly set. 15 | await sleep(1) 16 | assert await execute_on_http("EXISTS", "string_for_expire") == 0 17 | 18 | 19 | @mark.asyncio 20 | async def test_with_datetime(async_redis: Redis) -> None: 21 | assert ( 22 | await async_redis.expire("string_for_expire_dt", datetime.timedelta(seconds=1)) 23 | is True 24 | ) 25 | 26 | # Check if the expiry was correctly set. 27 | await sleep(1) 28 | assert await execute_on_http("EXISTS", "string_for_expire_dt") == 0 29 | 30 | 31 | @mark.asyncio 32 | async def test_xx(async_redis: Redis) -> None: 33 | # Must fail since it does not have an expiry. 34 | assert await async_redis.expire("string_without_expire", 1, xx=True) is False 35 | 36 | 37 | @mark.asyncio 38 | async def test_gt(async_redis: Redis) -> None: 39 | # Must fail since it 1 is not greater than infinity. 40 | assert await async_redis.expire("string_without_expire", 1, gt=True) is False 41 | -------------------------------------------------------------------------------- /tests/commands/asyncio/generic/test_expireat.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | from asyncio import sleep 3 | from time import time 4 | 5 | from pytest import mark 6 | 7 | from tests.execute_on_http import execute_on_http 8 | from upstash_redis.asyncio import Redis 9 | 10 | 11 | @mark.asyncio 12 | async def test(async_redis: Redis) -> None: 13 | # Set the expiry one second from the current time. 14 | assert ( 15 | await async_redis.expireat( 16 | "string_for_expireat", unix_time_seconds=int(time()) + 1 17 | ) 18 | is True 19 | ) 20 | 21 | await sleep(2) 22 | assert await execute_on_http("EXISTS", "string_for_expireat") == 0 23 | 24 | 25 | @mark.asyncio 26 | async def test_with_datetime(async_redis: Redis) -> None: 27 | assert ( 28 | await async_redis.expireat( 29 | "string_for_expireat_dt", 30 | datetime.datetime.now() + datetime.timedelta(seconds=1), 31 | ) 32 | is True 33 | ) 34 | 35 | # Check if the expiry was correctly set. 36 | await sleep(1) 37 | assert await execute_on_http("EXISTS", "string_for_expireat_dt") == 0 38 | -------------------------------------------------------------------------------- /tests/commands/asyncio/generic/test_keys.py: -------------------------------------------------------------------------------- 1 | from pytest import mark 2 | 3 | from upstash_redis.asyncio import Redis 4 | 5 | 6 | @mark.asyncio 7 | async def test(async_redis: Redis) -> None: 8 | assert await async_redis.keys(pattern="hash") == ["hash"] 9 | -------------------------------------------------------------------------------- /tests/commands/asyncio/generic/test_persist.py: -------------------------------------------------------------------------------- 1 | from pytest import mark 2 | 3 | from tests.execute_on_http import execute_on_http 4 | from upstash_redis.asyncio import Redis 5 | 6 | 7 | @mark.asyncio 8 | async def test(async_redis: Redis) -> None: 9 | await execute_on_http("EXPIRE", "string_for_persist", "5") 10 | 11 | assert await async_redis.persist("string_for_persist") is True 12 | 13 | # Check if the expiry was correctly removed. 14 | assert await execute_on_http("TTL", "string_for_persist") == -1 15 | -------------------------------------------------------------------------------- /tests/commands/asyncio/generic/test_pexpire.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | from asyncio import sleep 3 | 4 | from pytest import mark 5 | 6 | from tests.execute_on_http import execute_on_http 7 | from upstash_redis.asyncio import Redis 8 | 9 | 10 | @mark.asyncio 11 | async def test(async_redis: Redis) -> None: 12 | assert await async_redis.pexpire("string_for_pexpire", milliseconds=1000) is True 13 | 14 | # Check if the expiry was correctly set. 15 | await sleep(1) 16 | assert await execute_on_http("EXISTS", "string_for_pexpire") == 0 17 | 18 | 19 | @mark.asyncio 20 | async def test_with_datetime(async_redis: Redis) -> None: 21 | assert ( 22 | await async_redis.pexpire( 23 | "string_for_pexpire_dt", datetime.timedelta(milliseconds=200) 24 | ) 25 | is True 26 | ) 27 | 28 | # Check if the expiry was correctly set. 29 | await sleep(0.2) 30 | assert await execute_on_http("EXISTS", "string_for_pexpire_dt") == 0 31 | -------------------------------------------------------------------------------- /tests/commands/asyncio/generic/test_pexpireat.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | from asyncio import sleep 3 | from time import time 4 | 5 | from pytest import mark 6 | 7 | from tests.execute_on_http import execute_on_http 8 | from upstash_redis.asyncio import Redis 9 | 10 | 11 | @mark.asyncio 12 | async def test(async_redis: Redis) -> None: 13 | # Set the expiry one second from the current time. 14 | assert ( 15 | await async_redis.pexpireat( 16 | "string_for_pexpireat", unix_time_milliseconds=int(time() * 1000) + 1000 17 | ) 18 | is True 19 | ) 20 | 21 | await sleep(2) 22 | assert await execute_on_http("EXISTS", "string_for_pexpireat") == 0 23 | 24 | 25 | @mark.asyncio 26 | async def test_with_datetime(async_redis: Redis) -> None: 27 | assert ( 28 | await async_redis.pexpireat( 29 | "string_for_pexpireat_dt", 30 | datetime.datetime.now() + datetime.timedelta(milliseconds=200), 31 | ) 32 | is True 33 | ) 34 | 35 | # Check if the expiry was correctly set. 36 | await sleep(0.2) 37 | assert await execute_on_http("EXISTS", "string_for_pexpireat_dt") == 0 38 | -------------------------------------------------------------------------------- /tests/commands/asyncio/generic/test_pttl.py: -------------------------------------------------------------------------------- 1 | from pytest import mark 2 | 3 | from tests.execute_on_http import execute_on_http 4 | from upstash_redis.asyncio import Redis 5 | 6 | 7 | @mark.asyncio 8 | async def test(async_redis: Redis) -> None: 9 | await execute_on_http("EXPIRE", "string_for_ttl", "500") 10 | 11 | # < 1000 would rather indicate seconds. 12 | assert await async_redis.pttl("string_for_ttl") > 1000 13 | -------------------------------------------------------------------------------- /tests/commands/asyncio/generic/test_randomkey.py: -------------------------------------------------------------------------------- 1 | from pytest import mark 2 | 3 | from upstash_redis.asyncio import Redis 4 | 5 | 6 | @mark.asyncio 7 | async def test(async_redis: Redis) -> None: 8 | assert isinstance(await async_redis.randomkey(), str) 9 | -------------------------------------------------------------------------------- /tests/commands/asyncio/generic/test_rename.py: -------------------------------------------------------------------------------- 1 | from pytest import mark 2 | 3 | from tests.execute_on_http import execute_on_http 4 | from upstash_redis.asyncio import Redis 5 | 6 | 7 | @mark.asyncio 8 | async def test(async_redis: Redis) -> None: 9 | assert await async_redis.rename("string_for_rename", newkey="rename") is True 10 | 11 | assert await execute_on_http("GET", "rename") == "test" 12 | -------------------------------------------------------------------------------- /tests/commands/asyncio/generic/test_renamenx.py: -------------------------------------------------------------------------------- 1 | from pytest import mark 2 | 3 | from upstash_redis.asyncio import Redis 4 | 5 | 6 | @mark.asyncio 7 | async def test(async_redis: Redis) -> None: 8 | assert await async_redis.renamenx("string", newkey="string") is False 9 | -------------------------------------------------------------------------------- /tests/commands/asyncio/generic/test_scan.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | from pytest import mark 4 | 5 | from upstash_redis.asyncio import Redis 6 | 7 | 8 | @mark.asyncio 9 | async def test(async_redis: Redis) -> None: 10 | result = await async_redis.scan(cursor=0) 11 | assert isinstance(result[0], int) and isinstance(result[1], List) 12 | 13 | 14 | @mark.asyncio 15 | async def test_with_match(async_redis: Redis) -> None: 16 | assert await async_redis.scan(cursor=0, match="hash") == (0, ["hash"]) 17 | 18 | 19 | @mark.asyncio 20 | async def test_with_count(async_redis: Redis) -> None: 21 | assert len(await async_redis.scan(cursor=0, count=1)) == 2 22 | 23 | 24 | @mark.asyncio 25 | async def test_with_scan_type(async_redis: Redis) -> None: 26 | assert (await async_redis.scan(cursor=0, type="hash"))[1] == ["hash"] 27 | 28 | 29 | @mark.asyncio 30 | async def test_scan_multiple_times(async_redis: Redis) -> None: 31 | cursor, keys = await async_redis.scan(cursor=0, count=1) 32 | assert cursor != 0 33 | assert len(keys) == 1 34 | cursor, keys = await async_redis.scan(cursor=cursor, count=1) 35 | assert cursor != 0 36 | assert len(keys) == 1 37 | -------------------------------------------------------------------------------- /tests/commands/asyncio/generic/test_touch.py: -------------------------------------------------------------------------------- 1 | from pytest import mark, raises 2 | 3 | from upstash_redis.asyncio import Redis 4 | 5 | 6 | @mark.asyncio 7 | async def test(async_redis: Redis) -> None: 8 | assert await async_redis.touch("string") == 1 9 | 10 | 11 | @mark.asyncio 12 | async def test_without_keys(async_redis: Redis) -> None: 13 | with raises(Exception) as exception: 14 | await async_redis.touch() 15 | 16 | assert str(exception.value) == "At least one key must be specified." 17 | -------------------------------------------------------------------------------- /tests/commands/asyncio/generic/test_ttl.py: -------------------------------------------------------------------------------- 1 | from pytest import mark 2 | 3 | from tests.execute_on_http import execute_on_http 4 | from upstash_redis.asyncio import Redis 5 | 6 | 7 | @mark.asyncio 8 | async def test(async_redis: Redis) -> None: 9 | await execute_on_http("EXPIRE", "string_for_ttl", "5") 10 | 11 | # > 1000 would rather indicate milliseconds. 12 | assert await async_redis.ttl("string_for_ttl") < 1000 13 | -------------------------------------------------------------------------------- /tests/commands/asyncio/generic/test_type.py: -------------------------------------------------------------------------------- 1 | from pytest import mark 2 | 3 | from upstash_redis.asyncio import Redis 4 | 5 | 6 | @mark.asyncio 7 | async def test(async_redis: Redis) -> None: 8 | assert await async_redis.type("hash") == "hash" 9 | -------------------------------------------------------------------------------- /tests/commands/asyncio/generic/test_unlink.py: -------------------------------------------------------------------------------- 1 | from pytest import mark, raises 2 | 3 | from upstash_redis.asyncio import Redis 4 | 5 | 6 | @mark.asyncio 7 | async def test(async_redis: Redis) -> None: 8 | assert await async_redis.unlink("string_for_unlink_1", "string_for_unlink_2") == 2 9 | 10 | 11 | @mark.asyncio 12 | async def test_without_keys(async_redis: Redis) -> None: 13 | with raises(Exception) as exception: 14 | await async_redis.unlink() 15 | 16 | assert str(exception.value) == "At least one key must be specified." 17 | -------------------------------------------------------------------------------- /tests/commands/asyncio/geo/test_geoadd.py: -------------------------------------------------------------------------------- 1 | from pytest import mark, raises 2 | 3 | from tests.execute_on_http import execute_on_http 4 | from upstash_redis.asyncio import Redis 5 | 6 | 7 | @mark.asyncio 8 | async def test(async_redis: Redis) -> None: 9 | assert ( 10 | await async_redis.geoadd( 11 | "Geo", 12 | (13.361389, 38.115556, "Palermo"), 13 | ) 14 | == 1 15 | ) 16 | 17 | # Test if the key was set, and it's a Geospatial index. 18 | assert ( 19 | await execute_on_http("GEODIST", "test_geo_index", "Palermo", "Catania") 20 | == "166274.1516" 21 | ) 22 | 23 | 24 | @mark.asyncio 25 | async def test_with_nx(async_redis: Redis) -> None: 26 | assert ( 27 | await async_redis.geoadd( 28 | "test_geo_index", 29 | (15.087268, 37.502669, "Catania"), 30 | nx=True, 31 | ) 32 | == 0 33 | ) 34 | 35 | 36 | @mark.asyncio 37 | async def test_with_xx(async_redis: Redis) -> None: 38 | assert ( 39 | await async_redis.geoadd( 40 | "test_geo_index", 41 | (15.087268, 37.502669, "new_member"), 42 | xx=True, 43 | ) 44 | == 0 45 | ) 46 | 47 | 48 | @mark.asyncio 49 | async def test_with_ch(async_redis: Redis) -> None: 50 | assert ( 51 | await async_redis.geoadd( 52 | "test_geo_index", 53 | (43.486392, -35.283347, "random"), 54 | ch=True, 55 | ) 56 | == 1 57 | ) 58 | 59 | 60 | @mark.asyncio 61 | async def test_without_members(async_redis: Redis) -> None: 62 | with raises(Exception) as exception: 63 | await async_redis.geoadd("test_geo_index") 64 | 65 | assert str(exception.value) == "At least one member must be added." 66 | 67 | 68 | @mark.asyncio 69 | async def test_with_nx_and_xx(async_redis: Redis) -> None: 70 | with raises(Exception) as exception: 71 | await async_redis.geoadd( 72 | "test_geo_index", 73 | (15.087268, 37.502669, "new_member"), 74 | nx=True, 75 | xx=True, 76 | ) 77 | 78 | assert str(exception.value) == '"nx" and "xx" are mutually exclusive.' 79 | -------------------------------------------------------------------------------- /tests/commands/asyncio/geo/test_geodist.py: -------------------------------------------------------------------------------- 1 | from pytest import mark 2 | 3 | from upstash_redis.asyncio import Redis 4 | 5 | 6 | @mark.asyncio 7 | async def test(async_redis: Redis) -> None: 8 | assert ( 9 | await async_redis.geodist("test_geo_index", "Palermo", "Catania") == 166274.1516 10 | ) 11 | 12 | 13 | @mark.asyncio 14 | async def test_with_unit(async_redis: Redis) -> None: 15 | assert ( 16 | await async_redis.geodist("test_geo_index", "Palermo", "Catania", unit="KM") 17 | == 166.2742 18 | ) 19 | -------------------------------------------------------------------------------- /tests/commands/asyncio/geo/test_geohash.py: -------------------------------------------------------------------------------- 1 | from pytest import mark 2 | 3 | from upstash_redis.asyncio import Redis 4 | 5 | 6 | @mark.asyncio 7 | async def test(async_redis: Redis) -> None: 8 | assert await async_redis.geohash("test_geo_index", "Palermo") == ["sqc8b49rny0"] 9 | -------------------------------------------------------------------------------- /tests/commands/asyncio/geo/test_geopos.py: -------------------------------------------------------------------------------- 1 | from pytest import mark 2 | 3 | from upstash_redis.asyncio import Redis 4 | 5 | 6 | @mark.asyncio 7 | async def test(async_redis: Redis) -> None: 8 | assert await async_redis.geopos("test_geo_index", "Palermo") == [ 9 | (13.361389338970184, 38.115556395496299) 10 | ] 11 | -------------------------------------------------------------------------------- /tests/commands/asyncio/geo/test_georadiusbymember_ro.py: -------------------------------------------------------------------------------- 1 | from pytest import mark, raises 2 | 3 | from upstash_redis.asyncio import Redis 4 | from upstash_redis.utils import GeoSearchResult 5 | 6 | 7 | @mark.asyncio 8 | async def test(async_redis: Redis) -> None: 9 | assert await async_redis.georadiusbymember_ro( 10 | "test_geo_index", "Catania", 200, "KM" 11 | ) == [ 12 | "Palermo", 13 | "Catania", 14 | ] 15 | 16 | 17 | @mark.asyncio 18 | async def test_with_distance(async_redis: Redis) -> None: 19 | assert await async_redis.georadiusbymember_ro( 20 | "test_geo_index", 21 | "Catania", 22 | 200, 23 | unit="KM", 24 | withdist=True, 25 | ) == [ 26 | GeoSearchResult(member="Palermo", distance=166.2742), 27 | GeoSearchResult(member="Catania", distance=0.0), 28 | ] 29 | 30 | 31 | @mark.asyncio 32 | async def test_with_hash(async_redis: Redis) -> None: 33 | assert await async_redis.georadiusbymember_ro( 34 | "test_geo_index", 35 | "Catania", 36 | 200, 37 | unit="KM", 38 | withhash=True, 39 | ) == [ 40 | GeoSearchResult(member="Palermo", hash=3479099956230698), 41 | GeoSearchResult(member="Catania", hash=3479447370796909), 42 | ] 43 | 44 | 45 | @mark.asyncio 46 | async def test_with_coordinates(async_redis: Redis) -> None: 47 | assert await async_redis.georadiusbymember_ro( 48 | "test_geo_index", 49 | "Catania", 50 | 200, 51 | unit="KM", 52 | withcoord=True, 53 | ) == [ 54 | GeoSearchResult( 55 | member="Palermo", 56 | longitude=13.3613893389701841, 57 | latitude=38.115556395496299, 58 | ), 59 | GeoSearchResult( 60 | member="Catania", 61 | longitude=15.087267458438873, 62 | latitude=37.50266842333162, 63 | ), 64 | ] 65 | 66 | 67 | @mark.asyncio 68 | async def test_with_count(async_redis: Redis) -> None: 69 | assert await async_redis.georadiusbymember_ro( 70 | "test_geo_index", "Catania", 200, unit="KM", count=1 71 | ) == ["Catania"] 72 | 73 | 74 | @mark.asyncio 75 | async def test_with_any(async_redis: Redis) -> None: 76 | assert await async_redis.georadiusbymember_ro( 77 | "test_geo_index", 78 | "Catania", 79 | 200, 80 | unit="KM", 81 | count=1, 82 | any=True, 83 | ) == ["Palermo"] 84 | 85 | 86 | @mark.asyncio 87 | async def test_with_sort(async_redis: Redis) -> None: 88 | assert await async_redis.georadiusbymember_ro( 89 | "test_geo_index", 90 | "Catania", 91 | 200, 92 | unit="KM", 93 | order="ASC", 94 | ) == ["Catania", "Palermo"] 95 | 96 | 97 | @mark.asyncio 98 | async def test_with_invalid_parameters(async_redis: Redis) -> None: 99 | with raises(Exception) as exception: 100 | await async_redis.georadiusbymember_ro( 101 | "test_geo_index", 102 | "Catania", 103 | 200, 104 | unit="KM", 105 | count=None, 106 | any=True, 107 | ) 108 | 109 | assert str(exception.value) == '"any" can only be used together with "count".' 110 | -------------------------------------------------------------------------------- /tests/commands/asyncio/hyperloglog/test_pfadd.py: -------------------------------------------------------------------------------- 1 | from pytest import mark 2 | 3 | from tests.execute_on_http import execute_on_http 4 | from upstash_redis.asyncio import Redis 5 | 6 | 7 | @mark.asyncio 8 | async def test(async_redis: Redis) -> None: 9 | assert await async_redis.pfadd("pfadd", 1, "a") is True 10 | 11 | assert await execute_on_http("PFCOUNT", "pfadd") == 2 12 | -------------------------------------------------------------------------------- /tests/commands/asyncio/hyperloglog/test_pfcount.py: -------------------------------------------------------------------------------- 1 | from pytest import mark, raises 2 | 3 | from upstash_redis.asyncio import Redis 4 | 5 | 6 | @mark.asyncio 7 | async def test(async_redis: Redis) -> None: 8 | assert await async_redis.pfcount("hyperloglog") == 2 9 | 10 | 11 | @mark.asyncio 12 | async def test_without_keys(async_redis: Redis) -> None: 13 | with raises(Exception) as exception: 14 | await async_redis.pfcount() 15 | 16 | assert str(exception.value) == "At least one key must be specified." 17 | -------------------------------------------------------------------------------- /tests/commands/asyncio/hyperloglog/test_pfmerge.py: -------------------------------------------------------------------------------- 1 | from pytest import mark 2 | 3 | from tests.execute_on_http import execute_on_http 4 | from upstash_redis.asyncio import Redis 5 | 6 | 7 | @mark.asyncio 8 | async def test(async_redis: Redis) -> None: 9 | assert ( 10 | await async_redis.pfmerge( 11 | "pfmerge", "hyperloglog_for_pfmerge_1", "hyperloglog_for_pfmerge_2" 12 | ) 13 | is True 14 | ) 15 | 16 | assert await execute_on_http("PFCOUNT", "pfmerge") == 4 17 | -------------------------------------------------------------------------------- /tests/commands/asyncio/pubsub/test_publish.py: -------------------------------------------------------------------------------- 1 | from pytest import mark 2 | 3 | from upstash_redis.asyncio import Redis 4 | 5 | 6 | @mark.asyncio 7 | async def test(async_redis: Redis) -> None: 8 | assert await async_redis.publish("test", "hello") == 0 9 | -------------------------------------------------------------------------------- /tests/commands/asyncio/scripting/test_eval.py: -------------------------------------------------------------------------------- 1 | from pytest import mark 2 | 3 | from upstash_redis.asyncio import Redis 4 | 5 | 6 | @mark.asyncio 7 | async def test(async_redis: Redis) -> None: 8 | assert await async_redis.eval('return "hello world"') == "hello world" 9 | 10 | 11 | @mark.asyncio 12 | async def test_with_keys(async_redis: Redis) -> None: 13 | assert await async_redis.eval("return {KEYS[1], KEYS[2]}", keys=["a", "b"]) == [ 14 | "a", 15 | "b", 16 | ] 17 | 18 | 19 | @mark.asyncio 20 | async def test_with_arguments(async_redis: Redis) -> None: 21 | assert await async_redis.eval("return {ARGV[1], ARGV[2]}", args=["c", "d"]) == [ 22 | "c", 23 | "d", 24 | ] 25 | 26 | 27 | @mark.asyncio 28 | async def test_with_keys_and_arguments(async_redis: Redis) -> None: 29 | assert await async_redis.eval( 30 | "return {ARGV[1], KEYS[1]}", keys=["a"], args=["b"] 31 | ) == [ 32 | "b", 33 | "a", 34 | ] 35 | -------------------------------------------------------------------------------- /tests/commands/asyncio/scripting/test_eval_ro.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from pytest import mark 3 | from upstash_redis.errors import UpstashError 4 | from upstash_redis.asyncio import Redis 5 | 6 | 7 | @mark.asyncio 8 | async def test(async_redis: Redis) -> None: 9 | assert await async_redis.eval_ro('return "hello world"') == "hello world" 10 | 11 | 12 | @mark.asyncio 13 | async def test_with_keys(async_redis: Redis) -> None: 14 | assert await async_redis.eval_ro("return {KEYS[1], KEYS[2]}", keys=["a", "b"]) == [ 15 | "a", 16 | "b", 17 | ] 18 | 19 | 20 | @mark.asyncio 21 | async def test_with_arguments(async_redis: Redis) -> None: 22 | assert await async_redis.eval_ro("return {ARGV[1], ARGV[2]}", args=["c", "d"]) == [ 23 | "c", 24 | "d", 25 | ] 26 | 27 | 28 | @mark.asyncio 29 | async def test_with_keys_and_arguments(async_redis: Redis) -> None: 30 | assert await async_redis.eval_ro( 31 | "return {ARGV[1], KEYS[1]}", keys=["a"], args=["b"] 32 | ) == [ 33 | "b", 34 | "a", 35 | ] 36 | 37 | 38 | @mark.asyncio 39 | async def test_with_readonly_eval(async_redis: Redis) -> None: 40 | key = "mykey" 41 | value = "Hello, Redis!" 42 | 43 | await async_redis.set(key, value) 44 | 45 | assert ( 46 | await async_redis.eval_ro("return redis.call('GET', KEYS[1])", keys=[key]) 47 | == value 48 | ) 49 | 50 | 51 | @mark.asyncio 52 | async def test_with_write_eval(async_redis: Redis) -> None: 53 | key = "mykey" 54 | value = "Hello, Redis!" 55 | 56 | await async_redis.set(key, value) 57 | 58 | with pytest.raises(UpstashError): 59 | await async_redis.eval_ro("return redis.call('DEL', KEYS[1])", keys=[key]) 60 | -------------------------------------------------------------------------------- /tests/commands/asyncio/scripting/test_evalsha.py: -------------------------------------------------------------------------------- 1 | from pytest import mark 2 | 3 | from tests.execute_on_http import execute_on_http 4 | from upstash_redis.asyncio import Redis 5 | 6 | 7 | @mark.asyncio 8 | async def test(async_redis: Redis) -> None: 9 | sha1_digest = await execute_on_http("SCRIPT", "LOAD", 'return "hello world"') 10 | 11 | assert isinstance(sha1_digest, str) 12 | assert await async_redis.evalsha(sha1_digest) == "hello world" 13 | 14 | 15 | @mark.asyncio 16 | async def test_with_keys(async_redis: Redis) -> None: 17 | sha1_digest = await execute_on_http("SCRIPT", "LOAD", "return {KEYS[1], KEYS[2]}") 18 | 19 | assert isinstance(sha1_digest, str) 20 | assert await async_redis.evalsha(sha1_digest, keys=["a", "b"]) == ["a", "b"] 21 | 22 | 23 | @mark.asyncio 24 | async def test_with_arguments(async_redis: Redis) -> None: 25 | sha1_digest = await execute_on_http("SCRIPT", "LOAD", "return {ARGV[1], ARGV[2]}") 26 | 27 | assert isinstance(sha1_digest, str) 28 | assert await async_redis.evalsha(sha1_digest, args=["c", "d"]) == ["c", "d"] 29 | 30 | 31 | @mark.asyncio 32 | async def test_with_keys_and_arguments(async_redis: Redis) -> None: 33 | sha1_digest = await execute_on_http("SCRIPT", "LOAD", "return {ARGV[1], KEYS[1]}") 34 | 35 | assert isinstance(sha1_digest, str) 36 | assert await async_redis.evalsha(sha1_digest, keys=["a"], args=["b"]) == ["b", "a"] 37 | -------------------------------------------------------------------------------- /tests/commands/asyncio/scripting/test_evalsha_ro.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from pytest import mark 3 | 4 | from tests.execute_on_http import execute_on_http 5 | from upstash_redis.errors import UpstashError 6 | from upstash_redis.asyncio import Redis 7 | 8 | 9 | @mark.asyncio 10 | async def test(async_redis: Redis) -> None: 11 | sha1_digest = await execute_on_http("SCRIPT", "LOAD", 'return "hello world"') 12 | 13 | assert isinstance(sha1_digest, str) 14 | assert await async_redis.evalsha_ro(sha1_digest) == "hello world" 15 | 16 | 17 | @mark.asyncio 18 | async def test_with_keys(async_redis: Redis) -> None: 19 | sha1_digest = await execute_on_http("SCRIPT", "LOAD", "return {KEYS[1], KEYS[2]}") 20 | 21 | assert isinstance(sha1_digest, str) 22 | assert await async_redis.evalsha_ro(sha1_digest, keys=["a", "b"]) == ["a", "b"] 23 | 24 | 25 | @mark.asyncio 26 | async def test_with_arguments(async_redis: Redis) -> None: 27 | sha1_digest = await execute_on_http("SCRIPT", "LOAD", "return {ARGV[1], ARGV[2]}") 28 | 29 | assert isinstance(sha1_digest, str) 30 | assert await async_redis.evalsha_ro(sha1_digest, args=["c", "d"]) == ["c", "d"] 31 | 32 | 33 | @mark.asyncio 34 | async def test_with_keys_and_arguments(async_redis: Redis) -> None: 35 | sha1_digest = await execute_on_http("SCRIPT", "LOAD", "return {ARGV[1], KEYS[1]}") 36 | 37 | assert isinstance(sha1_digest, str) 38 | assert await async_redis.evalsha_ro(sha1_digest, keys=["a"], args=["b"]) == [ 39 | "b", 40 | "a", 41 | ] 42 | 43 | 44 | @mark.asyncio 45 | async def test_with_readonly_evalsha(async_redis: Redis) -> None: 46 | key = "mykey" 47 | value = "Hello, Redis!" 48 | 49 | await async_redis.set(key, value) 50 | sha1_digest = await execute_on_http( 51 | "SCRIPT", "LOAD", "return redis.call('GET', KEYS[1])" 52 | ) 53 | 54 | assert isinstance(sha1_digest, str) 55 | assert await async_redis.evalsha_ro(sha1_digest, keys=[key]) == value 56 | 57 | 58 | @mark.asyncio 59 | async def test_with_write_evalsha(async_redis: Redis) -> None: 60 | key = "mykey" 61 | value = "Hello, Redis!" 62 | 63 | await async_redis.set(key, value) 64 | sha1_digest = await execute_on_http( 65 | "SCRIPT", "LOAD", "return redis.call('DEL', KEYS[1])" 66 | ) 67 | 68 | assert isinstance(sha1_digest, str) 69 | with pytest.raises(UpstashError): 70 | await async_redis.evalsha_ro(sha1_digest, keys=[key]) 71 | -------------------------------------------------------------------------------- /tests/commands/asyncio/scripting/test_script_exists.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis.asyncio import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | async def flush_scripts(async_redis: Redis): 8 | await async_redis.script_flush() 9 | yield 10 | await async_redis.script_flush() 11 | 12 | 13 | @pytest.mark.asyncio 14 | async def test_script_exists(async_redis: Redis): 15 | sha1 = await async_redis.script_load("return 1") 16 | sha2 = await async_redis.script_load("return 2") 17 | 18 | result = await async_redis.script_exists(sha1, sha2) 19 | 20 | expected_result = [True, 1] 21 | assert result == expected_result 22 | 23 | result = await async_redis.script_exists("non_existing_sha") 24 | 25 | expected_result = [False] 26 | assert result == expected_result 27 | -------------------------------------------------------------------------------- /tests/commands/asyncio/scripting/test_script_flush.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis.asyncio import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | async def load_scripts(async_redis: Redis): 8 | await async_redis.script_flush() 9 | yield 10 | await async_redis.script_flush() 11 | 12 | 13 | @pytest.mark.asyncio 14 | async def test_script_flush(async_redis: Redis): 15 | script1 = await async_redis.script_load("return 1") 16 | script2 = await async_redis.script_load("return 2") 17 | 18 | result = await async_redis.script_exists(script1, script2) 19 | expected_result = [True, 1] 20 | assert result == expected_result 21 | 22 | await async_redis.script_flush() 23 | 24 | result = await async_redis.script_exists(script1, script2) 25 | 26 | expected_result = [False, False] 27 | assert result == expected_result 28 | -------------------------------------------------------------------------------- /tests/commands/asyncio/scripting/test_script_load.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis.asyncio import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | async def flush_scripts(async_redis: Redis): 8 | await async_redis.script_flush() 9 | yield 10 | await async_redis.script_flush() 11 | 12 | 13 | @pytest.mark.asyncio 14 | async def test_script_load(async_redis: Redis): 15 | script1 = "return 1" 16 | script2 = "return 2" 17 | 18 | script1_sha = await async_redis.script_load(script1) 19 | script2_sha = await async_redis.script_load(script2) 20 | 21 | res = await async_redis.evalsha(script1_sha) 22 | assert res == 1 23 | 24 | result = await async_redis.script_exists(script1_sha, script2_sha) 25 | 26 | expected_result = [True, True] 27 | assert result == expected_result 28 | -------------------------------------------------------------------------------- /tests/commands/asyncio/test_asyncio_pipeline.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import pytest_asyncio 3 | 4 | from upstash_redis.asyncio import Redis 5 | 6 | 7 | @pytest_asyncio.fixture(autouse=True) 8 | async def flush_db(async_redis: Redis): 9 | await async_redis.delete("rocket", "space", "marine") 10 | 11 | 12 | @pytest.mark.asyncio 13 | async def test_pipeline(async_redis: Redis): 14 | pipeline = async_redis.pipeline() 15 | 16 | pipeline.incr("rocket") 17 | pipeline.incr("rocket") 18 | pipeline.incr("space") 19 | pipeline.incr("rocket") 20 | pipeline.incr("space") 21 | pipeline.incr("rocket") 22 | 23 | # can chain commands 24 | pipeline.get("rocket").get("space").get("marine") 25 | 26 | res = await pipeline.exec() 27 | assert res == [1, 2, 1, 3, 2, 4, "4", "2", None] 28 | 29 | 30 | @pytest.mark.asyncio 31 | async def test_multi(async_redis: Redis): 32 | pipeline = async_redis.multi() 33 | 34 | pipeline.incr("rocket") 35 | pipeline.incr("rocket") 36 | pipeline.incr("space") 37 | pipeline.incr("rocket") 38 | pipeline.incr("space") 39 | pipeline.incr("rocket") 40 | 41 | pipeline.get("rocket") 42 | pipeline.get("space") 43 | pipeline.get("marine") 44 | 45 | res = await pipeline.exec() 46 | assert res == [1, 2, 1, 3, 2, 4, "4", "2", None] 47 | 48 | 49 | @pytest.mark.asyncio 50 | async def test_context_manager_usage(async_redis: Redis): 51 | async with async_redis.pipeline() as pipeline: 52 | pipeline.incr("rocket") 53 | pipeline.incr("rocket") 54 | pipeline.incr("space") 55 | pipeline.incr("rocket") 56 | pipeline.incr("space") 57 | pipeline.incr("rocket") 58 | result = await pipeline.exec() 59 | 60 | # add a command to the pipeline which will be 61 | # removed from the pipeline when we exit the context 62 | pipeline.set("foo", "bar") 63 | 64 | assert result == [1, 2, 1, 3, 2, 4] 65 | assert len(pipeline._command_stack) == 0 # pipeline is empty 66 | 67 | # redis still works after pipeline is done 68 | get_result = await async_redis.get("rocket") 69 | assert get_result == "4" 70 | 71 | get_pipeline = async_redis.pipeline() 72 | get_pipeline.get("rocket") 73 | get_pipeline.get("space") 74 | get_pipeline.get("marine") 75 | 76 | res = await get_pipeline.exec() 77 | assert res == ["4", "2", None] 78 | 79 | 80 | @pytest.mark.asyncio 81 | async def test_context_manager_raise(async_redis: Redis): 82 | """ 83 | Check that exceptions in context aren't silently ignored 84 | 85 | This can happen if we return something in __exit__ method 86 | """ 87 | with pytest.raises(Exception): 88 | async with async_redis.pipeline() as pipeline: 89 | pipeline.incr("rocket") 90 | raise Exception("test") 91 | 92 | 93 | @pytest.mark.asyncio 94 | async def test_run_pipeline_twice(async_redis: Redis): 95 | """ 96 | Runs a pipeline twice 97 | """ 98 | pipeline = async_redis.pipeline() 99 | pipeline.incr("albatros") 100 | result = await pipeline.exec() 101 | assert result == [1] 102 | 103 | pipeline.incrby("albatros", 2) 104 | result = await pipeline.exec() 105 | assert result == [3] 106 | -------------------------------------------------------------------------------- /tests/commands/hash/test_hdel.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_hash(redis: Redis): 8 | hash_name = "myhash" 9 | redis.delete(hash_name) 10 | yield 11 | redis.delete(hash_name) 12 | 13 | 14 | def test_hdel(redis: Redis): 15 | hash_name = "myhash" 16 | field1 = "field1" 17 | field2 = "field2" 18 | field3 = "field3" 19 | 20 | redis.hset(hash_name, field1, "value1") 21 | redis.hset(hash_name, field2, "value2") 22 | redis.hset(hash_name, field3, "value3") 23 | 24 | result = redis.hdel(hash_name, field2, field3) 25 | 26 | assert result == 2 # Number of fields deleted 27 | 28 | remaining_fields = redis.hkeys(hash_name) 29 | assert remaining_fields == [field1] # Only field1 should remain 30 | 31 | 32 | def test_hdel_with_pairs(redis: Redis): 33 | hash_name = "myhash" 34 | 35 | pairs = { 36 | "field1": "value1", 37 | "field2": "value2", 38 | "field3": "value3", 39 | } 40 | field1 = "field1" 41 | field2 = "field2" 42 | field3 = "field3" 43 | 44 | redis.hset(hash_name, values=pairs) 45 | 46 | result = redis.hdel(hash_name, field3) 47 | 48 | assert result == 1 49 | 50 | remaining_fields = redis.hkeys(hash_name) 51 | assert sorted(remaining_fields) == sorted([field1, field2]) 52 | -------------------------------------------------------------------------------- /tests/commands/hash/test_hexists.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_hash(redis: Redis): 8 | hash_name = "myhash" 9 | redis.delete(hash_name) 10 | 11 | 12 | def test_hexists(redis: Redis): 13 | hash_name = "myhash" 14 | field1 = "field1" 15 | field2 = "field2" 16 | 17 | redis.hset(hash_name, field1, "value1") 18 | 19 | exists_field1 = redis.hexists(hash_name, field1) 20 | exists_field2 = redis.hexists(hash_name, field2) 21 | 22 | assert exists_field1 is True 23 | assert exists_field2 is False 24 | -------------------------------------------------------------------------------- /tests/commands/hash/test_hexpireat.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import time 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_hash(redis: Redis): 8 | hash_name = "myhash" 9 | redis.delete(hash_name) 10 | 11 | 12 | def test_hexpireat_sets_expiry(redis: Redis): 13 | hash_name = "myhash" 14 | field = "field1" 15 | value = "value1" 16 | 17 | redis.hset(hash_name, field, value) 18 | future_timestamp = int(time.time()) + 2 19 | assert redis.hexpireat(hash_name, field, future_timestamp)[0] == 1 20 | 21 | time.sleep(3) 22 | assert redis.hexists(hash_name, field) == 0 23 | -------------------------------------------------------------------------------- /tests/commands/hash/test_hexpiretime.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import time 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_hash(redis: Redis): 8 | hash_name = "myhash" 9 | redis.delete(hash_name) 10 | 11 | 12 | def test_hexpiretime_returns_correct_expiry(redis: Redis): 13 | hash_name = "myhash" 14 | field = "field1" 15 | value = "value1" 16 | 17 | redis.hset(hash_name, field, value) 18 | redis.hexpire(hash_name, field, 5) 19 | expiry_time = redis.hexpiretime(hash_name, [field])[0] 20 | 21 | assert expiry_time > int(time.time()) 22 | assert expiry_time <= int(time.time()) + 5 23 | 24 | 25 | def test_hexpiretime_returns_minus1_if_no_expiry(redis: Redis): 26 | hash_name = "myhash" 27 | field = "field1" 28 | value = "value1" 29 | 30 | redis.hset(hash_name, field, value) 31 | assert redis.hexpiretime(hash_name, [field])[0] == -1 32 | 33 | 34 | def test_hexpiretime_returns_minus2_if_field_does_not_exist(redis: Redis): 35 | hash_name = "myhash" 36 | field = "field1" 37 | 38 | assert redis.hexpiretime(hash_name, [field])[0] == -2 39 | -------------------------------------------------------------------------------- /tests/commands/hash/test_hget.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_hash(redis: Redis): 8 | hash_name = "myhash" 9 | redis.delete(hash_name) 10 | yield 11 | redis.delete(hash_name) 12 | 13 | 14 | def test_hget(redis: Redis): 15 | hash_name = "myhash" 16 | field1 = "field1" 17 | field2 = "field2" 18 | 19 | redis.hset(hash_name, field1, "value1") 20 | redis.hset(hash_name, field2, "value2") 21 | 22 | value1 = redis.hget(hash_name, field1) 23 | value2 = redis.hget(hash_name, field2) 24 | value3 = redis.hget(hash_name, "non_existing_field") 25 | 26 | assert value1 == "value1" 27 | assert value2 == "value2" 28 | assert value3 is None 29 | -------------------------------------------------------------------------------- /tests/commands/hash/test_hgetall.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_hash(redis: Redis): 8 | hash_name = "myhash" 9 | redis.delete(hash_name) 10 | yield 11 | redis.delete(hash_name) 12 | 13 | 14 | def test_hgetall(redis: Redis): 15 | hash_name = "myhash" 16 | fields_values = {"field1": "value1", "field2": "value2"} 17 | 18 | redis.hset(hash_name, values=fields_values) 19 | 20 | result = redis.hgetall(hash_name) 21 | 22 | assert isinstance(result, dict) 23 | 24 | assert result == fields_values 25 | -------------------------------------------------------------------------------- /tests/commands/hash/test_hincrby.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_hash(redis: Redis): 8 | hash_name = "myhash" 9 | redis.delete(hash_name) 10 | 11 | 12 | def test_hincrby(redis: Redis): 13 | hash_name = "myhash" 14 | field = "counter" 15 | 16 | # Set an initial value for the field 17 | redis.hset(hash_name, field, str(5)) 18 | 19 | # Increment the field value by a specific amount 20 | result = redis.hincrby(hash_name, field, 3) 21 | 22 | assert result == 8 23 | 24 | # Additional assertions can be added here based on the expected behavior of HINCRBY command 25 | -------------------------------------------------------------------------------- /tests/commands/hash/test_hincrbyfloat.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_hash(redis: Redis): 8 | hash_name = "myhash" 9 | redis.delete(hash_name) 10 | 11 | 12 | def test_hincrbyfloat(redis: Redis): 13 | hash_name = "myhash" 14 | field = "float_counter" 15 | 16 | # Set an initial value for the field 17 | redis.hset(hash_name, field, "2.5") 18 | 19 | # Increment the field value by a specific floating-point amount 20 | result = redis.hincrbyfloat(hash_name, field, 1.2) 21 | 22 | assert isinstance(result, float) 23 | 24 | assert result == pytest.approx(3.7) 25 | -------------------------------------------------------------------------------- /tests/commands/hash/test_hkeys.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_hash(redis: Redis): 8 | hash_name = "myhash" 9 | redis.delete(hash_name) 10 | yield 11 | redis.delete(hash_name) 12 | 13 | 14 | def test_hkeys(redis: Redis): 15 | hash_name = "myhash" 16 | 17 | redis.hset(hash_name, "field1", "value1") 18 | redis.hset(hash_name, "field2", "value2") 19 | redis.hset(hash_name, "field3", "value3") 20 | 21 | result = redis.hkeys(hash_name) 22 | 23 | assert sorted(result) == sorted(["field1", "field2", "field3"]) 24 | -------------------------------------------------------------------------------- /tests/commands/hash/test_hlen.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_hash(redis: Redis): 8 | hash_name = "myhash" 9 | redis.delete(hash_name) 10 | yield 11 | redis.delete(hash_name) 12 | 13 | 14 | def test_hlen(redis: Redis): 15 | hash_name = "myhash" 16 | 17 | # Set some fields in the hash 18 | redis.hset(hash_name, "field1", "value1") 19 | redis.hset(hash_name, "field2", "value2") 20 | redis.hset(hash_name, "field3", "value3") 21 | 22 | # Get the length of the hash 23 | result = redis.hlen(hash_name) 24 | 25 | assert result == 3 26 | -------------------------------------------------------------------------------- /tests/commands/hash/test_hmget.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_hash(redis: Redis): 8 | hash_name = "myhash" 9 | redis.delete(hash_name) 10 | yield 11 | redis.delete(hash_name) 12 | 13 | 14 | def test_hmget(redis: Redis): 15 | hash_name = "myhash" 16 | 17 | # Set some fields in the hash 18 | redis.hset(hash_name, "field1", "value1") 19 | redis.hset(hash_name, "field2", "value2") 20 | redis.hset(hash_name, "field3", "value3") 21 | 22 | # Get multiple field values from the hash 23 | fields = ["field1", "field3", "non_existing_field"] 24 | result = redis.hmget(hash_name, *fields) 25 | 26 | expected_result = ["value1", "value3", None] 27 | assert result == expected_result 28 | 29 | # Additional assertions can be added here based on the expected behavior of HMGET command 30 | -------------------------------------------------------------------------------- /tests/commands/hash/test_hmset.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_hash(redis: Redis) -> None: 8 | hash_name = "myhash" 9 | redis.delete(hash_name) 10 | 11 | 12 | def test_hmset(redis: Redis) -> None: 13 | hash_name = "myhash" 14 | 15 | # Define the field-value pairs to set in the hash 16 | fields = {"field1": "value1", "field2": "value2", "field3": "value3"} 17 | 18 | # Set the field-value pairs in the hash using HMSET command 19 | result = redis.hmset(hash_name, fields) 20 | 21 | assert result is True 22 | 23 | assert redis.hmget(hash_name, *fields) == ["value1", "value2", "value3"] 24 | -------------------------------------------------------------------------------- /tests/commands/hash/test_hpersist.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from upstash_redis import Redis 3 | 4 | 5 | @pytest.fixture(autouse=True) 6 | def flush_hash(redis: Redis): 7 | hash_name = "myhash" 8 | redis.delete(hash_name) 9 | 10 | 11 | def test_hpersist_removes_expiry(redis: Redis): 12 | hash_name = "myhash" 13 | field = "field1" 14 | value = "value1" 15 | 16 | redis.hset(hash_name, field, value) 17 | redis.hexpire(hash_name, field, 500) 18 | ttl_before = redis.httl(hash_name, field) 19 | assert ttl_before[0] > 0 20 | 21 | redis.hpersist(hash_name, field) 22 | ttl_after = redis.httl(hash_name, field) 23 | assert ttl_after == [-1] 24 | 25 | 26 | def test_hpersist_does_nothing_if_no_expiry(redis: Redis): 27 | hash_name = "myhash" 28 | field = "field1" 29 | value = "value1500" 30 | 31 | redis.hset(hash_name, field, value) 32 | assert redis.hpersist(hash_name, field) == [-1] 33 | -------------------------------------------------------------------------------- /tests/commands/hash/test_hpexpire.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import time 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_hash(redis: Redis): 8 | hash_name = "myhash" 9 | redis.delete(hash_name) 10 | 11 | 12 | def test_hpexpire_sets_expiry_in_milliseconds(redis: Redis): 13 | hash_name = "myhash" 14 | field = "field1" 15 | value = "value1" 16 | 17 | redis.hset(hash_name, field, value) 18 | assert redis.hpexpire(hash_name, field, 500) == [1] 19 | 20 | time.sleep(1) 21 | assert redis.hget(hash_name, field) is None 22 | 23 | 24 | def test_hpexpire_does_not_set_expiry_if_field_does_not_exist(redis: Redis): 25 | hash_name = "myhash" 26 | field = "field1" 27 | 28 | assert redis.hpexpire(hash_name, field, 500) == [-2] 29 | 30 | 31 | def test_hpexpire_overwrites_existing_expiry(redis: Redis): 32 | hash_name = "myhash" 33 | field = "field1" 34 | value = "value1" 35 | 36 | redis.hset(hash_name, field, value) 37 | redis.hpexpire(hash_name, field, 1000) 38 | assert redis.hpexpire(hash_name, field, 2000) == [1] 39 | 40 | time.sleep(2.5) 41 | assert redis.hget(hash_name, field) is None 42 | -------------------------------------------------------------------------------- /tests/commands/hash/test_hpexpireat.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import time 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_hash(redis: Redis): 8 | hash_name = "myhash" 9 | redis.delete(hash_name) 10 | 11 | 12 | def test_hpexpireat_sets_expiry_in_milliseconds(redis: Redis): 13 | hash_name = "myhash" 14 | field = "field1" 15 | value = "value1" 16 | 17 | redis.hset(hash_name, field, value) 18 | future_timestamp_ms = int(time.time() * 1000) + 500 19 | assert redis.hpexpireat(hash_name, field, future_timestamp_ms) == [1] 20 | 21 | time.sleep(1) 22 | assert redis.hget(hash_name, field) is None 23 | -------------------------------------------------------------------------------- /tests/commands/hash/test_hpexpiretime.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import time 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_hash(redis: Redis): 8 | hash_name = "myhash" 9 | redis.delete(hash_name) 10 | 11 | 12 | def test_hpexpiretime_returns_correct_expiry(redis: Redis): 13 | hash_name = "myhash" 14 | field = "field1" 15 | value = "value1" 16 | 17 | redis.hset(hash_name, field, value) 18 | redis.hpexpire(hash_name, field, 500) 19 | expiry_time_ms = redis.hpexpiretime(hash_name, [field])[0] 20 | 21 | assert expiry_time_ms > 0 22 | assert expiry_time_ms <= int(time.time() * 1000) + 1000 23 | 24 | 25 | def test_hpexpiretime_returns_minus1_if_no_expiry(redis: Redis): 26 | hash_name = "myhash" 27 | field = "field1" 28 | value = "value1" 29 | 30 | redis.hset(hash_name, field, value) 31 | assert redis.hpexpiretime(hash_name, [field])[0] == -1 32 | 33 | 34 | def test_hpexpiretime_returns_minus2_if_field_does_not_exist(redis: Redis): 35 | hash_name = "myhash" 36 | field = "field1" 37 | 38 | assert redis.hpexpiretime(hash_name, [field])[0] == -2 39 | -------------------------------------------------------------------------------- /tests/commands/hash/test_hpttl.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from upstash_redis import Redis 3 | 4 | 5 | @pytest.fixture(autouse=True) 6 | def flush_hash(redis: Redis): 7 | hash_name = "myhash" 8 | redis.delete(hash_name) 9 | 10 | 11 | def test_hpttl_returns_correct_ttl(redis: Redis): 12 | hash_name = "myhash" 13 | field = "field1" 14 | value = "value1" 15 | 16 | redis.hset(hash_name, field, value) 17 | redis.hpexpire(hash_name, field, 1500) 18 | ttl = redis.hpttl(hash_name, [field])[0] 19 | 20 | assert ttl > 0 21 | 22 | 23 | def test_hpttl_returns_minus1_if_no_expiry(redis: Redis): 24 | hash_name = "myhash" 25 | field = "field1" 26 | value = "value1" 27 | 28 | redis.hset(hash_name, field, value) 29 | assert redis.hpttl(hash_name, [field])[0] == -1 30 | 31 | 32 | def test_hpttl_returns_minus2_if_field_does_not_exist(redis: Redis): 33 | hash_name = "myhash" 34 | field = "field1" 35 | 36 | assert redis.hpttl(hash_name, [field])[0] == -2 37 | -------------------------------------------------------------------------------- /tests/commands/hash/test_hrandfield.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_hash(redis: Redis): 8 | hash_name = "myhash" 9 | redis.delete(hash_name) 10 | 11 | 12 | def test_hrandfield_single(redis: Redis) -> None: 13 | hash_name = "myhash" 14 | 15 | # Add fields to the hash 16 | redis.hset(hash_name, "field1", "value1") 17 | redis.hset(hash_name, "field2", "value2") 18 | redis.hset(hash_name, "field3", "value3") 19 | 20 | # Get a single random field from the hash 21 | result = redis.hrandfield(hash_name) 22 | assert result in ["field1", "field2", "field3"] 23 | 24 | 25 | def test_hrandfield_multiple(redis: Redis) -> None: 26 | hash_name = "myhash" 27 | 28 | # Add fields to the hash 29 | redis.hset(hash_name, "field1", "value1") 30 | redis.hset(hash_name, "field2", "value2") 31 | redis.hset(hash_name, "field3", "value3") 32 | 33 | # Get multiple random fields from the hash 34 | count = 1 # Number of random fields to retrieve 35 | result = redis.hrandfield(hash_name, count=count) 36 | 37 | assert result is not None 38 | assert len(result) == count # Number of random fields returned 39 | assert all(field in ["field1", "field2", "field3"] for field in result) 40 | 41 | result = redis.hrandfield(hash_name, count=2, withvalues=True) 42 | assert isinstance(result, dict) 43 | 44 | for key, val in result.items(): 45 | assert redis.hget(hash_name, key) == val 46 | -------------------------------------------------------------------------------- /tests/commands/hash/test_hscan.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_hash(redis: Redis): 8 | hash_name = "myhash" 9 | redis.delete(hash_name) 10 | yield 11 | redis.delete(hash_name) 12 | 13 | 14 | def test_hscan_with_match(redis: Redis): 15 | hash_name = "myhash" 16 | 17 | # Set some field-value pairs in the hash 18 | redis.hset(hash_name, "field1", "value1") 19 | redis.hset(hash_name, "field2", "value2") 20 | redis.hset(hash_name, "field3", "value3") 21 | redis.hset(hash_name, "other_field", "other_value") 22 | 23 | # Use HSCAN command with match parameter to filter field-value pairs 24 | cursor = 0 25 | count = 2 # Maximum number of elements to retrieve per iteration 26 | pattern = "field*" # Pattern to match field names 27 | result = {} 28 | 29 | while True: 30 | cursor, data = redis.hscan(hash_name, cursor, count=count, match=pattern) 31 | result.update(data) 32 | 33 | if cursor == 0: 34 | break 35 | 36 | # Assertions to check the retrieved field-value pairs 37 | assert result == {"field1": "value1", "field2": "value2", "field3": "value3"} 38 | -------------------------------------------------------------------------------- /tests/commands/hash/test_hset.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_hash(redis: Redis): 8 | hash_name = "myhash" 9 | redis.delete(hash_name) 10 | yield 11 | redis.delete(hash_name) 12 | 13 | 14 | def test_hset(redis: Redis): 15 | hash_name = "myhash" 16 | 17 | # Use HSET command to set field-value pairs in the hash 18 | result = redis.hset(hash_name, "field1", "value1") 19 | assert result == 1 # 1 if field is a new field in the hash and value was set 20 | assert redis.hget(hash_name, "field1") == "value1" 21 | 22 | # Use HSET command to update an existing field 23 | result = redis.hset(hash_name, "field1", "updated_value") 24 | assert result == 0 # 0 if field already existed in the hash and value was updated 25 | assert redis.hget(hash_name, "field1") == "updated_value" 26 | 27 | # Use HSET command to set multiple field-value pairs at once 28 | field_value_pairs = {"field2": "value2", "field3": "value3"} 29 | result = redis.hset(hash_name, values=field_value_pairs) 30 | assert result == 2 # Number of fields added to the hash 31 | assert redis.hget(hash_name, "field2") == "value2" 32 | assert redis.hget(hash_name, "field3") == "value3" 33 | 34 | with pytest.raises(Exception): 35 | redis.hset("test_name", "asd") 36 | -------------------------------------------------------------------------------- /tests/commands/hash/test_hsetnx.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_hash(redis: Redis): 8 | hash_name = "myhash" 9 | redis.delete(hash_name) 10 | 11 | 12 | def test_hsetnx(redis: Redis): 13 | hash_name = "myhash" 14 | 15 | result = redis.hsetnx(hash_name, "field1", "value1") 16 | assert result is True 17 | assert redis.hget(hash_name, "field1") == "value1" 18 | 19 | result = redis.hsetnx(hash_name, "field1", "new_value") 20 | assert result is False 21 | assert redis.hget(hash_name, "field1") == "value1" 22 | -------------------------------------------------------------------------------- /tests/commands/hash/test_hstrlen.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_hash(redis: Redis): 8 | hash_name = "myhash" 9 | redis.delete(hash_name) 10 | 11 | 12 | def test_hstrlen(redis: Redis): 13 | hash_name = "myhash" 14 | redis.hmset(hash_name, {"first": "123456", "second": "123"}) 15 | 16 | assert redis.hstrlen(hash_name, "first") == 6 17 | assert redis.hstrlen(hash_name, "second") == 3 18 | -------------------------------------------------------------------------------- /tests/commands/hash/test_httl.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from upstash_redis import Redis 3 | 4 | 5 | @pytest.fixture(autouse=True) 6 | def flush_hash(redis: Redis): 7 | hash_name = "myhash" 8 | redis.delete(hash_name) 9 | 10 | 11 | def test_httl_returns_correct_ttl(redis: Redis): 12 | hash_name = "myhash" 13 | field = "field1" 14 | value = "value1" 15 | 16 | redis.hset(hash_name, field, value) 17 | redis.hexpire(hash_name, field, 5) 18 | ttl = redis.httl(hash_name, [field])[0] 19 | 20 | assert ttl > 0 21 | assert ttl <= 5 22 | 23 | 24 | def test_httl_returns_minus1_if_no_expiry(redis: Redis): 25 | hash_name = "myhash" 26 | field = "field1" 27 | value = "value1" 28 | 29 | redis.hset(hash_name, field, value) 30 | assert redis.httl(hash_name, [field])[0] == -1 31 | 32 | 33 | def test_httl_returns_minus2_if_field_does_not_exist(redis: Redis): 34 | hash_name = "myhash" 35 | field = "field1" 36 | 37 | assert redis.httl(hash_name, [field])[0] == -2 38 | -------------------------------------------------------------------------------- /tests/commands/hash/test_hvals.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_hash(redis: Redis): 8 | hash_name = "myhash" 9 | redis.delete(hash_name) 10 | yield 11 | redis.delete(hash_name) 12 | 13 | 14 | def test_hvals(redis: Redis): 15 | hash_name = "myhash" 16 | 17 | redis.hset(hash_name, "field1", "value1") 18 | redis.hset(hash_name, "field2", "value2") 19 | redis.hset(hash_name, "field3", "value3") 20 | 21 | result = redis.hvals(hash_name) 22 | 23 | assert sorted(result) == sorted(["value1", "value2", "value3"]) 24 | -------------------------------------------------------------------------------- /tests/commands/json/test_json_arrappend.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from upstash_redis import Redis 3 | from upstash_redis.typing import JSONValueT 4 | from typing import List 5 | 6 | 7 | @pytest.fixture(autouse=True) 8 | def setup_json(redis: Redis): 9 | json_key = "json_arrappend" 10 | value: JSONValueT = {"int": 1, "array": [], "object": {"array": [1]}} 11 | redis.json.set(json_key, "$", value) 12 | yield 13 | redis.delete(json_key) 14 | 15 | 16 | def test_arrappend_single_element(redis: Redis): 17 | key = "json_arrappend" 18 | path = "$.array" 19 | 20 | assert redis.json.arrappend(key, path, 1) == [1] 21 | assert redis.json.arrappend(key, path, "new val") == [2] 22 | assert redis.json.arrappend(key, path, 1.5) == [3] 23 | assert redis.json.arrappend(key, path, True) == [4] 24 | assert redis.json.arrappend(key, path, [1]) == [5] 25 | assert redis.json.arrappend(key, path, {"key": "value"}) == [6] 26 | assert redis.json.get(key, path) == [ 27 | [1, "new val", 1.5, True, [1], {"key": "value"}] 28 | ] 29 | 30 | 31 | def test_arrappend_multiple_elements(redis: Redis): 32 | key = "json_arrappend" 33 | path = "$.array" 34 | new_values: List[JSONValueT] = [1, "new val", 1.5, True, [1], {"key": "value"}] 35 | 36 | assert redis.json.arrappend(key, path, *new_values) == [6] 37 | assert redis.json.get(key, path) == [ 38 | [1, "new val", 1.5, True, [1], {"key": "value"}] 39 | ] 40 | 41 | 42 | def test_arrappend_nonarray_path(redis: Redis): 43 | key = "json_arrappend" 44 | path = "$.int" 45 | new_value = 9 46 | 47 | assert redis.json.arrappend(key, path, new_value) == [None] 48 | assert redis.json.get(key, path) == [1] 49 | 50 | 51 | def test_arrappend_wildcard(redis: Redis): 52 | key = "json_arrappend" 53 | path = "$..array" 54 | new_value = 9 55 | 56 | assert redis.json.arrappend(key, path, new_value) == [2, 1] 57 | assert redis.json.get(key, path) == [[1, 9], [9]] 58 | -------------------------------------------------------------------------------- /tests/commands/json/test_json_arrindex.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from upstash_redis import Redis 3 | from upstash_redis.typing import JSONValueT 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def setup_json(redis: Redis): 8 | json_key = "json_arrindex" 9 | value: JSONValueT = { 10 | "array": [1, "test", ["a"], 1.5, {"test": 1}], 11 | "int": 1, 12 | "object": {"array": [0, 1]}, 13 | } 14 | redis.json.set(json_key, "$", value) 15 | yield 16 | redis.delete(json_key) 17 | 18 | 19 | def test_arrindex_existing_element(redis: Redis): 20 | key = "json_arrindex" 21 | path = "$.array" 22 | 23 | assert redis.json.arrindex(key, path, 1) == [0] 24 | assert redis.json.arrindex(key, path, "test") == [1] 25 | assert redis.json.arrindex(key, path, ["a"]) == [2] 26 | assert redis.json.arrindex(key, path, 1.5) == [3] 27 | assert redis.json.arrindex(key, path, {"test": 1}) == [4] 28 | 29 | 30 | def test_arrindex_nonexisting_element(redis: Redis): 31 | key = "json_arrindex" 32 | path = "$.array" 33 | value = 4 34 | 35 | assert redis.json.arrindex(key, path, value) == [-1] 36 | 37 | 38 | def test_arrindex_nonarray_path(redis: Redis): 39 | key = "json_arrindex" 40 | path = "$.int" 41 | value = 4 42 | 43 | assert redis.json.arrindex(key, path, value) == [None] 44 | 45 | 46 | def test_arrindex_wildcard(redis: Redis): 47 | key = "json_arrindex" 48 | path = "$..array" 49 | value = 1 50 | 51 | assert redis.json.arrindex(key, path, value) == [1, 0] 52 | -------------------------------------------------------------------------------- /tests/commands/json/test_json_arrinsert.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from upstash_redis import Redis 3 | from upstash_redis.typing import JSONValueT 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def setup_json(redis: Redis): 8 | json_key = "json_arrinsert" 9 | value: JSONValueT = {"int": 1, "array": [], "object": {"array": [0]}} 10 | redis.json.set(json_key, "$", value) 11 | yield 12 | redis.delete(json_key) 13 | 14 | 15 | def test_arrinsert_single_element(redis: Redis): 16 | key = "json_arrinsert" 17 | path = "$.array" 18 | 19 | assert redis.json.arrinsert(key, path, 0, 1) == [1] 20 | assert redis.json.arrinsert(key, path, 0, "new val") == [2] 21 | assert redis.json.arrinsert(key, path, 0, 1.5) == [3] 22 | assert redis.json.arrinsert(key, path, 0, True) == [4] 23 | assert redis.json.arrinsert(key, path, 0, [1]) == [5] 24 | assert redis.json.arrinsert(key, path, 0, {"key": "value"}) == [6] 25 | assert redis.json.get(key, path) == [ 26 | [{"key": "value"}, [1], True, 1.5, "new val", 1] 27 | ] 28 | 29 | 30 | def test_arrinsert_multiple_elements(redis: Redis): 31 | key = "json_arrinsert" 32 | path = "$.array" 33 | new_values = [5, 6, 7] 34 | 35 | assert redis.json.arrinsert(key, path, 0, *new_values) == [3] 36 | assert redis.json.get(key, path) == [[5, 6, 7]] 37 | 38 | 39 | def test_arrinsert_nonarray_path(redis: Redis): 40 | key = "json_arrinsert" 41 | path = "$.int" 42 | new_value = 9 43 | index = 0 44 | 45 | assert redis.json.arrinsert(key, path, index, new_value) == [None] 46 | assert redis.json.get(key, path) == [1] 47 | 48 | 49 | def test_arrinsert_wildcard(redis: Redis): 50 | key = "json_arrinsert" 51 | path = "$..array" 52 | new_value = 9 53 | index = 0 54 | 55 | assert redis.json.arrinsert(key, path, index, new_value) == [2, 1] 56 | assert redis.json.get(key, path) == [[9, 0], [9]] 57 | -------------------------------------------------------------------------------- /tests/commands/json/test_json_arrlen.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from upstash_redis import Redis 3 | from upstash_redis.typing import JSONValueT 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def setup_json(redis: Redis): 8 | json_key = "json_arrlen" 9 | value: JSONValueT = { 10 | "array": [1, 2, 3, 4], 11 | "int": 1, 12 | "object": {"array": [1, 2, 3]}, 13 | } 14 | redis.json.set(json_key, "$", value) 15 | yield 16 | redis.delete(json_key) 17 | 18 | 19 | def test_arrlen_existing_element(redis: Redis): 20 | key = "json_arrlen" 21 | path = "$.array" 22 | 23 | length = redis.json.arrlen(key, path) 24 | assert length == [4] 25 | 26 | 27 | def test_arrlen_nonarray_path(redis: Redis): 28 | key = "json_arrlen" 29 | path = "$.int" 30 | 31 | length = redis.json.arrlen(key, path) 32 | assert length == [None] 33 | 34 | 35 | def test_arrlen_wildcard(redis: Redis): 36 | key = "json_arrlen" 37 | path = "$..array" 38 | 39 | length = redis.json.arrlen(key, path) 40 | assert length == [3, 4] 41 | -------------------------------------------------------------------------------- /tests/commands/json/test_json_arrpop.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from upstash_redis import Redis 3 | from upstash_redis.typing import JSONValueT 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def setup_json(redis: Redis): 8 | json_key = "json_arrpop" 9 | value: JSONValueT = { 10 | "int": 1, 11 | "array": [1, 2, 3, 4], 12 | "object": {"array": [5, 6, 7]}, 13 | } 14 | redis.json.set(json_key, "$", value) 15 | yield 16 | redis.delete(json_key) 17 | 18 | 19 | def test_arrpop_single_element(redis: Redis): 20 | key = "json_arrpop" 21 | path = "$.array" 22 | index = 0 23 | 24 | removed_element = redis.json.arrpop(key, path, index) 25 | assert removed_element == [1] 26 | assert redis.json.get(key, path) == [[2, 3, 4]] 27 | 28 | 29 | def test_arrpop_negative_index(redis: Redis): 30 | key = "json_arrpop" 31 | path = "$.array" 32 | index = -2 33 | 34 | removed_element = redis.json.arrpop(key, path, index) 35 | assert removed_element == [3] 36 | assert redis.json.get(key, path) == [[1, 2, 4]] 37 | 38 | 39 | def test_arrpop_out_of_range_index(redis: Redis): 40 | key = "json_arrpop" 41 | path = "$.array" 42 | index = 4 43 | 44 | removed_element = redis.json.arrpop(key, path, index) 45 | assert removed_element == [4] 46 | assert redis.json.get(key, path) == [[1, 2, 3]] 47 | 48 | 49 | def test_arrpop_nonarray_path(redis: Redis): 50 | key = "json_arrpop" 51 | path = "$.int" 52 | index = 0 53 | 54 | removed_element = redis.json.arrpop(key, path, index) 55 | assert removed_element == [None] 56 | assert redis.json.get(key, path) == [1] 57 | 58 | 59 | def test_arrpop_wildcard(redis: Redis): 60 | key = "json_arrpop" 61 | path = "$..array" 62 | index = 1 63 | 64 | removed_element = redis.json.arrpop(key, path, index) 65 | assert removed_element == [6, 2] 66 | assert redis.json.get(key, path) == [[5, 7], [1, 3, 4]] 67 | -------------------------------------------------------------------------------- /tests/commands/json/test_json_arrtrim.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from upstash_redis import Redis 3 | from upstash_redis.typing import JSONValueT 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def setup_json(redis: Redis): 8 | json_key = "json_arrtrim" 9 | value: JSONValueT = { 10 | "int": 1, 11 | "array": [1, 2, 3, 4], 12 | "object": {"array": [5, 6, 7]}, 13 | } 14 | redis.json.set(json_key, "$", value) 15 | yield 16 | redis.delete(json_key) 17 | 18 | 19 | def test_arrtrim_default(redis: Redis): 20 | key = "json_arrtrim" 21 | path = "$.array" 22 | start = 1 23 | stop = 2 24 | 25 | newlen = redis.json.arrtrim(key, path, start, stop) 26 | assert newlen == [2] 27 | assert redis.json.get(key, path) == [[2, 3]] 28 | 29 | 30 | def test_arrtrim_negative_index(redis: Redis): 31 | key = "json_arrtrim" 32 | path = "$.array" 33 | start = -2 34 | stop = -1 35 | 36 | newlen = redis.json.arrtrim(key, path, start, stop) 37 | assert newlen == [2] 38 | assert redis.json.get(key, path) == [[3, 4]] 39 | 40 | 41 | def test_arrtrim_out_of_range_index(redis: Redis): 42 | key = "json_arrtrim" 43 | path = "$.array" 44 | start = 4 45 | stop = 5 46 | 47 | newlen = redis.json.arrtrim(key, path, start, stop) 48 | assert newlen == [0] 49 | assert redis.json.get(key, path) == [[]] 50 | 51 | 52 | def test_arrtrim_start_greater_than_stop(redis: Redis): 53 | key = "json_arrtrim" 54 | path = "$.array" 55 | start = 1 56 | stop = 0 57 | 58 | newlen = redis.json.arrtrim(key, path, start, stop) 59 | assert newlen == [0] 60 | assert redis.json.get(key, path) == [[]] 61 | 62 | 63 | def test_arrtrim_nonarray_path(redis: Redis): 64 | key = "json_arrtrim" 65 | path = "$.int" 66 | start = 0 67 | stop = 0 68 | 69 | newlen = redis.json.arrtrim(key, path, start, stop) 70 | assert newlen == [None] 71 | assert redis.json.get(key, path) == [1] 72 | 73 | 74 | def test_arrtrim_wildcard(redis: Redis): 75 | key = "json_arrtrim" 76 | path = "$..array" 77 | start = 0 78 | stop = 1 79 | 80 | newlen = redis.json.arrtrim(key, path, start, stop) 81 | assert newlen == [2, 2] 82 | assert redis.json.get(key, path) == [[5, 6], [1, 2]] 83 | -------------------------------------------------------------------------------- /tests/commands/json/test_json_clear.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from upstash_redis import Redis 3 | from upstash_redis.typing import JSONValueT 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def setup_json(redis: Redis): 8 | json_key = "json_clear" 9 | value: JSONValueT = { 10 | "int": 1, 11 | "array": [1, 2, 3, 4], 12 | "object": {"array": [1, 2, 3]}, 13 | } 14 | redis.json.set(json_key, "$", value) 15 | yield 16 | redis.delete(json_key) 17 | 18 | 19 | def test_clear_single_element(redis: Redis): 20 | key = "json_clear" 21 | path = "$.array" 22 | 23 | removed_element = redis.json.clear(key, path) 24 | assert removed_element == 1 25 | assert redis.json.get(key) == [ 26 | {"int": 1, "array": [], "object": {"array": [1, 2, 3]}} 27 | ] 28 | 29 | 30 | def test_clear_wildcard(redis: Redis): 31 | key = "json_clear" 32 | path = "$..array" 33 | 34 | removed_element = redis.json.clear(key, path) 35 | assert removed_element == 2 36 | assert redis.json.get(key) == [{"int": 1, "object": {"array": []}, "array": []}] 37 | -------------------------------------------------------------------------------- /tests/commands/json/test_json_delete.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from upstash_redis import Redis 3 | from upstash_redis.typing import JSONValueT 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def setup_json(redis: Redis): 8 | json_key = "json_delete" 9 | value: JSONValueT = { 10 | "int": 1, 11 | "array": [1, 2, 3, 4], 12 | "object": {"array": [1, 2, 3]}, 13 | } 14 | redis.json.set(json_key, "$", value) 15 | yield 16 | redis.delete(json_key) 17 | 18 | 19 | def test_delete_single_element(redis: Redis): 20 | key = "json_delete" 21 | path = "$.array" 22 | 23 | removed_element = redis.json.delete(key, path) 24 | assert removed_element == 1 25 | assert redis.json.get(key) == [{"int": 1, "object": {"array": [1, 2, 3]}}] 26 | 27 | 28 | def test_delete_wildcard(redis: Redis): 29 | key = "json_delete" 30 | path = "$..array" 31 | 32 | removed_element = redis.json.delete(key, path) 33 | assert removed_element == 2 34 | assert redis.json.get(key) == [{"int": 1, "object": {}}] 35 | -------------------------------------------------------------------------------- /tests/commands/json/test_json_forget.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from upstash_redis import Redis 3 | from upstash_redis.typing import JSONValueT 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def setup_json(redis: Redis): 8 | json_key = "json_forget" 9 | value: JSONValueT = { 10 | "int": 1, 11 | "array": [1, 2, 3, 4], 12 | "object": {"array": [1, 2, 3]}, 13 | } 14 | redis.json.set(json_key, "$", value) 15 | yield 16 | redis.delete(json_key) 17 | 18 | 19 | def test_forget_single_element(redis: Redis): 20 | key = "json_forget" 21 | path = "$.array" 22 | 23 | removed_element = redis.json.forget(key, path) 24 | assert removed_element == 1 25 | assert redis.json.get(key) == [{"int": 1, "object": {"array": [1, 2, 3]}}] 26 | 27 | 28 | def test_forget_wildcard(redis: Redis): 29 | key = "json_forget" 30 | path = "$..array" 31 | 32 | removed_element = redis.json.forget(key, path) 33 | assert removed_element == 2 34 | assert redis.json.get(key) == [{"int": 1, "object": {}}] 35 | -------------------------------------------------------------------------------- /tests/commands/json/test_json_get.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from upstash_redis import Redis 3 | from upstash_redis.typing import JSONValueT 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def setup_json(redis: Redis): 8 | json_key = "json_get" 9 | value: JSONValueT = { 10 | "int": 1, 11 | "array": [1, 2, 3, 4], 12 | "object": {"array": [1, 2, 3]}, 13 | } 14 | redis.json.set(json_key, "$", value) 15 | yield 16 | redis.delete(json_key) 17 | 18 | 19 | def test_get(redis: Redis): 20 | key = "json_get" 21 | 22 | assert redis.json.get(key) == [ 23 | {"int": 1, "array": [1, 2, 3, 4], "object": {"array": [1, 2, 3]}} 24 | ] 25 | 26 | 27 | def test_get_path(redis: Redis): 28 | key = "json_get" 29 | path = "$.array" 30 | assert redis.json.get(key, path) == [[1, 2, 3, 4]] 31 | 32 | 33 | def test_get_multiple_path(redis: Redis): 34 | key = "json_get" 35 | paths = ["$.array", "$.object", "$.int"] 36 | assert redis.json.get(key, *paths) == { 37 | "$.array": [[1, 2, 3, 4]], 38 | "$.object": [{"array": [1, 2, 3]}], 39 | "$.int": [1], 40 | } 41 | 42 | 43 | def test_get_nonexisting_key(redis: Redis): 44 | key = "json_get" 45 | path = "$.nonexisting_key" 46 | 47 | assert redis.json.get(key, path) == [] 48 | 49 | 50 | def test_get_wildcard(redis: Redis): 51 | key = "json_get" 52 | path = "$..array" 53 | 54 | assert redis.json.get(key, path) == [[1, 2, 3], [1, 2, 3, 4]] 55 | -------------------------------------------------------------------------------- /tests/commands/json/test_json_merge.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | from upstash_redis.typing import JSONValueT 5 | 6 | 7 | @pytest.fixture(autouse=True) 8 | def setup_json(redis: Redis): 9 | json_key = "json_merge" 10 | value: JSONValueT = { 11 | "int": 1, 12 | "array": [1, 2, 3, 4], 13 | "object": {"array": [1, 2, 3]}, 14 | } 15 | redis.json.set(json_key, "$", value) 16 | yield 17 | redis.delete(json_key) 18 | 19 | 20 | def test_merge(redis: Redis): 21 | key = "json_merge" 22 | 23 | assert redis.json.merge(key, "$", {"str": "test", "int": 2}) is True 24 | assert redis.json.get(key) == [ 25 | {"int": 2, "array": [1, 2, 3, 4], "object": {"array": [1, 2, 3]}, "str": "test"} 26 | ] 27 | 28 | 29 | def test_merge_nonexisting_key(redis: Redis): 30 | key = "json_merge_nonexisting" 31 | 32 | assert redis.json.merge(key, "$", {"str": "test", "int": 2}) is True 33 | assert redis.json.get(key) == [{"int": 2, "str": "test"}] 34 | 35 | 36 | def test_merge_wildcard(redis: Redis): 37 | key = "json_merge" 38 | path = "$..array" 39 | 40 | assert redis.json.merge(key, path, [2, 2, 3, 4]) is True 41 | assert redis.json.get(key) == [ 42 | {"int": 1, "array": [2, 2, 3, 4], "object": {"array": [2, 2, 3, 4]}} 43 | ] 44 | -------------------------------------------------------------------------------- /tests/commands/json/test_json_mget.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | from upstash_redis.typing import JSONValueT 5 | 6 | 7 | @pytest.fixture(autouse=True) 8 | def setup_json(redis: Redis): 9 | json_keys = ["json_mget_1", "json_mget_2"] 10 | value1: JSONValueT = { 11 | "int": 1, 12 | "array": [1, 2, 3, 4], 13 | "object": {"array": [1, 2, 3]}, 14 | } 15 | value2: JSONValueT = { 16 | "int": 2, 17 | "array": [2, 2, 3, 4], 18 | "object": {"array": [2, 2, 3]}, 19 | } 20 | redis.json.set(json_keys[0], "$", value1) 21 | redis.json.set(json_keys[1], "$", value2) 22 | yield 23 | for json_key in json_keys: 24 | redis.delete(json_key) 25 | 26 | 27 | def test_mget(redis: Redis): 28 | keys = ["json_mget_1", "json_mget_2"] 29 | path = "$.int" 30 | 31 | assert redis.json.mget(keys, path) == [[1], [2]] 32 | 33 | 34 | def test_mget_nonexisting_key(redis: Redis): 35 | keys = ["json_mget_1", "json_mget_2"] 36 | path = "$.nonexisting_key" 37 | 38 | assert redis.json.mget(keys, path) == [[], []] 39 | 40 | 41 | def test_mget_wildcard(redis: Redis): 42 | keys = ["json_mget_1", "json_mget_2"] 43 | path = "$..array" 44 | 45 | assert redis.json.mget(keys, path) == [ 46 | [[1, 2, 3], [1, 2, 3, 4]], 47 | [[2, 2, 3], [2, 2, 3, 4]], 48 | ] 49 | -------------------------------------------------------------------------------- /tests/commands/json/test_json_mset.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | from upstash_redis.typing import JSONValueT 5 | 6 | 7 | @pytest.fixture(autouse=True) 8 | def setup_json(redis: Redis): 9 | redis.json.delete("json_mset_1") 10 | redis.json.delete("json_mset_2") 11 | yield 12 | redis.json.delete("json_mset_1") 13 | redis.json.delete("json_mset_2") 14 | 15 | 16 | def test_mset(redis: Redis): 17 | json_key_1 = "json_mset_1" 18 | json_key_2 = "json_mset_2" 19 | value1: JSONValueT = { 20 | "int": 1, 21 | "array": [1, 2, 3, 4], 22 | "object": {"array": [1, 2, 3]}, 23 | } 24 | value2: JSONValueT = { 25 | "int": 2, 26 | "array": [2, 2, 3, 4], 27 | "object": {"array": [2, 2, 3]}, 28 | } 29 | 30 | assert ( 31 | redis.json.mset([(json_key_1, "$", value1), (json_key_2, "$", value2)]) is True 32 | ) 33 | assert redis.json.mget([json_key_1, json_key_2], "$.int") == [[1], [2]] 34 | assert redis.json.mset([(json_key_1, "$.int", 2), (json_key_2, "$.int", 3)]) is True 35 | assert redis.json.mget([json_key_1, json_key_2], "$.int") == [[2], [3]] 36 | assert ( 37 | redis.json.mset( 38 | [(json_key_1, "$..array[0]", 2), (json_key_2, "$..array[0]", 3)] 39 | ) 40 | is True 41 | ) 42 | assert redis.json.mget([json_key_1, json_key_2], "$..array[0]") == [[2, 2], [3, 3]] 43 | -------------------------------------------------------------------------------- /tests/commands/json/test_json_numincrby.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from upstash_redis import Redis 3 | from upstash_redis.typing import JSONValueT 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def setup_json(redis: Redis): 8 | json_key = "json_numincrby" 9 | value: JSONValueT = {"int": 1, "str": "test", "object": {"int": 2}} 10 | redis.json.set(json_key, "$", value) 11 | yield 12 | redis.delete(json_key) 13 | 14 | 15 | def test_numincrby(redis: Redis): 16 | key = "json_numincrby" 17 | path = "$.int" 18 | increment = 1 19 | 20 | assert redis.json.numincrby(key, path, increment) == [2] 21 | assert redis.json.get(key, path) == [2] 22 | 23 | 24 | def test_numincrby_nonintkey(redis: Redis): 25 | key = "json_numincrby" 26 | path = "$.str" 27 | increment = 1 28 | 29 | assert redis.json.numincrby(key, path, increment) == [None] 30 | 31 | 32 | def test_numincrby_nonexisting_key(redis: Redis): 33 | key = "json_numincrby" 34 | path = "$.nonexisting_key" 35 | increment = 1 36 | 37 | assert redis.json.numincrby(key, path, increment) == [] 38 | assert redis.json.get(key, path) == [] 39 | 40 | 41 | def test_numincrby_wildcard(redis: Redis): 42 | key = "json_numincrby" 43 | path = "$..int" 44 | increment = 1 45 | 46 | assert redis.json.numincrby(key, path, increment) == [3, 2] 47 | assert redis.json.get(key, path) == [3, 2] 48 | -------------------------------------------------------------------------------- /tests/commands/json/test_json_nummultby.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from upstash_redis import Redis 3 | from upstash_redis.typing import JSONValueT 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def setup_json(redis: Redis): 8 | json_key = "json_nummultby" 9 | value: JSONValueT = {"int": 2, "str": "test", "object": {"int": 3}} 10 | redis.json.set(json_key, "$", value) 11 | yield 12 | redis.delete(json_key) 13 | 14 | 15 | def test_nummultby(redis: Redis): 16 | key = "json_nummultby" 17 | path = "$.int" 18 | mult = 3 19 | 20 | assert redis.json.nummultby(key, path, mult) == [6] 21 | assert redis.json.get(key, path) == [6] 22 | 23 | 24 | def test_nummultby_nonintkey(redis: Redis): 25 | key = "json_nummultby" 26 | path = "$.str" 27 | mult = 1 28 | 29 | assert redis.json.nummultby(key, path, mult) == [None] 30 | 31 | 32 | def test_nummultby_nonexisting_key(redis: Redis): 33 | key = "json_nummultby" 34 | path = "$.nonexisting_key" 35 | mult = 3 36 | 37 | assert redis.json.nummultby(key, path, mult) == [] 38 | assert redis.json.get(key, path) == [] 39 | 40 | 41 | def test_nummultby_wildcard(redis: Redis): 42 | key = "json_nummultby" 43 | path = "$..int" 44 | mult = 3 45 | 46 | assert redis.json.nummultby(key, path, mult) == [9, 6] 47 | assert redis.json.get(key, path) == [9, 6] 48 | -------------------------------------------------------------------------------- /tests/commands/json/test_json_objkeys.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | from upstash_redis.typing import JSONValueT 5 | 6 | 7 | @pytest.fixture(autouse=True) 8 | def setup_json(redis: Redis): 9 | json_key = "json_objkeys" 10 | value: JSONValueT = { 11 | "int": 2, 12 | "str": "test", 13 | "object": {"int": 3}, 14 | "second_object": {"object": {"str": "test", "int": 2}}, 15 | } 16 | redis.json.set(json_key, "$", value) 17 | yield 18 | redis.delete(json_key) 19 | 20 | 21 | def test_objkeys(redis: Redis): 22 | key = "json_objkeys" 23 | path = "$" 24 | 25 | response = redis.json.objkeys(key, path) 26 | 27 | assert response[0] is not None 28 | response[0].sort() 29 | 30 | assert response == [["int", "object", "second_object", "str"]] 31 | 32 | 33 | def test_objkeys_nonobjkey(redis: Redis): 34 | key = "json_objkeys" 35 | path = "$.int" 36 | 37 | assert redis.json.objkeys(key, path) == [None] 38 | 39 | 40 | def test_objkeys_nonexisting_key(redis: Redis): 41 | key = "json_objkeys" 42 | path = "$.nonexisting_key" 43 | 44 | assert redis.json.objkeys(key, path) == [] 45 | 46 | 47 | def test_objkeys_wildcard(redis: Redis): 48 | key = "json_objkeys" 49 | path = "$..object" 50 | 51 | response = redis.json.objkeys(key, path) 52 | for i in response: 53 | assert i is not None 54 | i.sort() 55 | 56 | response.sort() 57 | 58 | assert response == [["int"], ["int", "str"]] 59 | -------------------------------------------------------------------------------- /tests/commands/json/test_json_objlen.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from upstash_redis import Redis 3 | from upstash_redis.typing import JSONValueT 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def setup_json(redis: Redis): 8 | json_key = "json_objlen" 9 | value: JSONValueT = { 10 | "int": 2, 11 | "str": "test", 12 | "object": {"int": 3}, 13 | "second_object": {"object": {"str": "test", "int": 2}}, 14 | } 15 | redis.json.set(json_key, "$", value) 16 | yield 17 | redis.delete(json_key) 18 | 19 | 20 | def test_objlen(redis: Redis): 21 | key = "json_objlen" 22 | path = "$" 23 | 24 | assert redis.json.objlen(key, path) == [4] 25 | 26 | 27 | def test_objlen_nonobjkey(redis: Redis): 28 | key = "json_objlen" 29 | path = "$.int" 30 | 31 | assert redis.json.objlen(key, path) == [None] 32 | 33 | 34 | def test_objlen_nonexisting_key(redis: Redis): 35 | key = "json_objlen" 36 | path = "$.nonexisting_key" 37 | 38 | assert redis.json.objlen(key, path) == [] 39 | 40 | 41 | def test_objlen_wildcard(redis: Redis): 42 | key = "json_objlen" 43 | path = "$..object" 44 | 45 | assert redis.json.objlen(key, path) == [2, 1] 46 | -------------------------------------------------------------------------------- /tests/commands/json/test_json_resp.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from upstash_redis import Redis 3 | from upstash_redis.typing import JSONValueT 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def setup_json(redis: Redis): 8 | json_key = "json_resp" 9 | value: JSONValueT = {"object": {"array": [1, 2, 3]}} 10 | redis.json.set(json_key, "$", value) 11 | yield 12 | redis.delete(json_key) 13 | 14 | 15 | def test_resp(redis: Redis): 16 | key = "json_resp" 17 | 18 | assert redis.json.resp(key, "$") == [ 19 | ["{", "object", ["{", "array", ["[", 1, 2, 3]]] 20 | ] 21 | -------------------------------------------------------------------------------- /tests/commands/json/test_json_set.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import upstash_redis.errors 4 | from upstash_redis import Redis 5 | 6 | 7 | @pytest.fixture(autouse=True) 8 | def setup_json(redis: Redis): 9 | json_key = "json_set" 10 | redis.delete(json_key) 11 | yield 12 | redis.delete(json_key) 13 | 14 | 15 | def test_set(redis: Redis): 16 | key = "json_set" 17 | 18 | assert redis.json.get(key) is None 19 | assert redis.json.set(key, "$", {"test": 1}) is True 20 | assert redis.json.set(key, "$.str", "test") is True 21 | assert redis.json.set(key, "$.int", 1) is True 22 | assert redis.json.set(key, "$.bool", True) is True 23 | assert redis.json.set(key, "$.array", [1, 2, "test"]) is True 24 | assert redis.json.set(key, "$.object", {"int": 1}) is True 25 | assert redis.json.set(key, "$.null", None) is True 26 | assert redis.json.get(key) == [ 27 | { 28 | "int": 1, 29 | "test": 1, 30 | "str": "test", 31 | "bool": True, 32 | "array": [1, 2, "test"], 33 | "object": {"int": 1}, 34 | "null": None, 35 | } 36 | ] 37 | 38 | 39 | def test_set_nonexisting_path(redis: Redis): 40 | key = "json_set" 41 | path = "$.nonexisting_key.str" 42 | 43 | assert redis.json.set(key, "$", {"int": 1}) is True 44 | assert redis.json.get(key) == [{"int": 1}] 45 | with pytest.raises(upstash_redis.errors.UpstashError): 46 | redis.json.set(key, path, "test") 47 | 48 | 49 | def test_set_wildcard(redis: Redis): 50 | key = "json_set" 51 | path = "$..int" 52 | 53 | assert redis.json.set(key, "$", {"int": 1, "obj": {"int": 3}}) is True 54 | assert redis.json.get(key) == [{"int": 1, "obj": {"int": 3}}] 55 | assert redis.json.set(key, path, 2) is True 56 | assert redis.json.get(key) == [{"int": 2, "obj": {"int": 2}}] 57 | 58 | 59 | def test_set_nx(redis: Redis): 60 | key = "json_set" 61 | path = "$.int" 62 | 63 | assert redis.json.set(key, "$", {"int": 1}) is True 64 | assert redis.json.get(key) == [{"int": 1}] 65 | assert redis.json.set(key, "$", {"str": "test"}, nx=True) is False 66 | assert redis.json.get(key) == [{"int": 1}] 67 | assert redis.json.set(key, path, 2, nx=True) is False 68 | assert redis.json.get(key) == [{"int": 1}] 69 | 70 | 71 | def test_set_xx(redis: Redis): 72 | key = "json_set" 73 | 74 | assert redis.json.set(key, "$", {"int": 1}, xx=True) is False 75 | assert redis.json.get(key) is None 76 | assert redis.json.set(key, "$", {"int": 1}) is True 77 | assert redis.json.get(key) == [{"int": 1}] 78 | assert redis.json.set(key, "$", {"str": "test"}, xx=True) is True 79 | assert redis.json.get(key) == [{"str": "test"}] 80 | -------------------------------------------------------------------------------- /tests/commands/json/test_json_strappend.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from upstash_redis import Redis 3 | 4 | 5 | @pytest.fixture(autouse=True) 6 | def setup_json(redis: Redis): 7 | json_key = "json_strappend" 8 | redis.json.set(json_key, "$", {"str": "test", "obj": {"str": "test_1"}}) 9 | yield 10 | redis.delete(json_key) 11 | 12 | 13 | def test_strappend(redis: Redis): 14 | key = "json_strappend" 15 | path = "$.str" 16 | 17 | assert redis.json.strappend(key, path, "_append") == [11] 18 | assert redis.json.get(key, path) == ["test_append"] 19 | 20 | 21 | def test_strappend_nonstr_path(redis: Redis): 22 | key = "json_strappend" 23 | path = "$.obj" 24 | 25 | assert redis.json.strappend(key, path, "_append") == [None] 26 | 27 | 28 | def test_strappend_wildcard(redis: Redis): 29 | key = "json_strappend" 30 | path = "$..str" 31 | 32 | assert redis.json.strappend(key, path, "_append") == [13, 11] 33 | assert redis.json.get(key, path) == ["test_1_append", "test_append"] 34 | -------------------------------------------------------------------------------- /tests/commands/json/test_json_strlen.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from upstash_redis import Redis 3 | 4 | 5 | @pytest.fixture(autouse=True) 6 | def setup_json(redis: Redis): 7 | json_key = "json_strlen" 8 | redis.json.set(json_key, "$", {"str": "test", "obj": {"str": "test_1"}}) 9 | yield 10 | redis.delete(json_key) 11 | 12 | 13 | def test_strlen(redis: Redis): 14 | key = "json_strlen" 15 | path = "$.str" 16 | 17 | assert redis.json.strlen(key, path) == [4] 18 | 19 | 20 | def test_strlen_nonstr_path(redis: Redis): 21 | key = "json_strlen" 22 | path = "$.obj" 23 | 24 | assert redis.json.strlen(key, path) == [None] 25 | 26 | 27 | def test_strlen_wildcard(redis: Redis): 28 | key = "json_strlen" 29 | path = "$..str" 30 | 31 | assert redis.json.strlen(key, path) == [6, 4] 32 | -------------------------------------------------------------------------------- /tests/commands/json/test_json_toggle.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def setup_json(redis: Redis): 8 | json_key = "json_toggle" 9 | redis.json.set(json_key, "$", {"bool": True, "obj": {"bool": False}}) 10 | yield 11 | redis.delete(json_key) 12 | 13 | 14 | def test_toggle(redis: Redis): 15 | key = "json_toggle" 16 | path = "$.bool" 17 | 18 | assert redis.json.toggle(key, path) == [False] 19 | assert redis.json.get(key, path) == [False] 20 | assert redis.json.toggle(key, path) == [True] 21 | assert redis.json.get(key, path) == [True] 22 | 23 | 24 | def test_toggle_nonbool_path(redis: Redis): 25 | key = "json_toggle" 26 | path = "$.obj" 27 | 28 | assert redis.json.toggle(key, path) == [None] 29 | 30 | 31 | def test_toggle_wildcard(redis: Redis): 32 | key = "json_toggle" 33 | path = "$..bool" 34 | 35 | assert redis.json.toggle(key, path) == [True, False] 36 | assert redis.json.get(key, path) == [True, False] 37 | assert redis.json.toggle(key, path) == [False, True] 38 | assert redis.json.get(key, path) == [False, True] 39 | -------------------------------------------------------------------------------- /tests/commands/json/test_json_type.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from upstash_redis import Redis 3 | from upstash_redis.typing import JSONValueT 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def setup_json(redis: Redis): 8 | json_key = "json_type" 9 | value: JSONValueT = { 10 | "number": 1, 11 | "array": [1, 2, 3, 4], 12 | "object": {"number": 3.1415}, 13 | "str": "test", 14 | "bool": True, 15 | "null": None, 16 | } 17 | redis.json.set(json_key, "$", value) 18 | yield 19 | redis.delete(json_key) 20 | 21 | 22 | def test_type_int(redis: Redis): 23 | key = "json_type" 24 | path = "$.number" 25 | 26 | assert redis.json.type(key, path) == ["integer"] 27 | 28 | 29 | def test_type_array(redis: Redis): 30 | key = "json_type" 31 | path = "$.array" 32 | 33 | assert redis.json.type(key, path) == ["array"] 34 | 35 | 36 | def test_type_object(redis: Redis): 37 | key = "json_type" 38 | path = "$.object" 39 | 40 | assert redis.json.type(key, path) == ["object"] 41 | 42 | 43 | def test_type_float(redis: Redis): 44 | key = "json_type" 45 | path = "$.object.number" 46 | 47 | assert redis.json.type(key, path) == ["number"] 48 | 49 | 50 | def test_type_str(redis: Redis): 51 | key = "json_type" 52 | path = "$.str" 53 | 54 | assert redis.json.type(key, path) == ["string"] 55 | 56 | 57 | def test_type_bool(redis: Redis): 58 | key = "json_type" 59 | path = "$.bool" 60 | 61 | assert redis.json.type(key, path) == ["boolean"] 62 | 63 | 64 | def test_type_null(redis: Redis): 65 | key = "json_type" 66 | path = "$.null" 67 | 68 | assert redis.json.type(key, path) == ["null"] 69 | 70 | 71 | def test_type_nonexisting_key(redis: Redis): 72 | key = "json_type" 73 | path = "$.nonexisting_key" 74 | 75 | assert redis.json.type(key, path) == [] 76 | 77 | 78 | def test_type_wildcard(redis: Redis): 79 | key = "json_type" 80 | path = "$..number" 81 | 82 | assert redis.json.type(key, path) == ["number", "integer"] 83 | -------------------------------------------------------------------------------- /tests/commands/list/test_lindex.py: -------------------------------------------------------------------------------- 1 | from pytest import fixture 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @fixture(autouse=True) 7 | def setup_teardown(redis: Redis): 8 | redis.rpush("mylist", "value1", "value2", "value3") 9 | yield 10 | redis.delete("mylist") 11 | 12 | 13 | def test_lindex_existing_index(redis: Redis): 14 | result = redis.lindex("mylist", 0) 15 | assert result == "value1" 16 | 17 | 18 | def test_lindex_invalid_index(redis: Redis): 19 | result = redis.lindex("mylist", 10) 20 | assert result is None 21 | 22 | 23 | def test_lindex_empty_list(redis: Redis): 24 | redis.delete("mylist") 25 | 26 | result = redis.lindex("mylist", 0) 27 | assert result is None 28 | -------------------------------------------------------------------------------- /tests/commands/list/test_linsert.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_mylists(redis: Redis): 8 | mylists = ["mylist1", "mylist2", "mylist3", "mylist4"] 9 | 10 | for mylist in mylists: 11 | redis.delete(mylist) 12 | 13 | 14 | def test_linsert_before_existing_value(redis: Redis): 15 | mylist = "mylist1" 16 | values = ["value1", "value2", "value3"] 17 | 18 | redis.rpush(mylist, *values) 19 | 20 | result = redis.linsert(mylist, "BEFORE", "value2", "new_value") 21 | assert result == 4 22 | assert redis.lrange(mylist, 0, -1) == ["value1", "new_value", "value2", "value3"] 23 | 24 | 25 | def test_linsert_after_existing_value(redis: Redis): 26 | mylist = "mylist2" 27 | values = ["value1", "value2", "value3"] 28 | 29 | redis.rpush(mylist, *values) 30 | 31 | result = redis.linsert(mylist, "AFTER", "value2", "new_value") 32 | assert result == 4 33 | assert redis.lrange(mylist, 0, -1) == ["value1", "value2", "new_value", "value3"] 34 | 35 | 36 | def test_linsert_before_nonexistent_value(redis: Redis): 37 | mylist = "mylist3" 38 | values = ["value1", "value2", "value3"] 39 | 40 | redis.rpush(mylist, *values) 41 | 42 | result = redis.linsert(mylist, "BEFORE", "value4", "new_value") 43 | assert result == -1 44 | assert redis.lrange(mylist, 0, -1) == ["value1", "value2", "value3"] 45 | 46 | 47 | def test_linsert_after_nonexistent_value(redis: Redis): 48 | mylist = "mylist4" 49 | values = ["value1", "value2", "value3"] 50 | 51 | redis.rpush(mylist, *values) 52 | 53 | result = redis.linsert(mylist, "AFTER", "value4", "new_value") 54 | assert result == -1 55 | assert redis.lrange(mylist, 0, -1) == ["value1", "value2", "value3"] 56 | -------------------------------------------------------------------------------- /tests/commands/list/test_llen.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_mylists(redis: Redis): 8 | mylists = ["mylist1", "mylist2", "mylist3", "mylist4"] 9 | 10 | for mylist in mylists: 11 | redis.delete(mylist) 12 | 13 | 14 | def test_llen_empty_list(redis: Redis): 15 | mylist = "mylist1" 16 | 17 | result = redis.llen(mylist) 18 | assert result == 0 19 | 20 | 21 | def test_llen_non_empty_list(redis: Redis): 22 | mylist = "mylist2" 23 | values = ["value1", "value2", "value3"] 24 | 25 | redis.rpush(mylist, *values) 26 | 27 | result = redis.llen(mylist) 28 | assert result == 3 29 | 30 | 31 | def test_llen_nonexistent_list(redis: Redis): 32 | mylist = "mylist3" 33 | 34 | result = redis.llen(mylist) 35 | assert result == 0 36 | 37 | 38 | def test_llen_after_deletion(redis: Redis): 39 | mylist = "mylist4" 40 | values = ["value1", "value2", "value3"] 41 | 42 | redis.rpush(mylist, *values) 43 | redis.delete(mylist) 44 | 45 | result = redis.llen(mylist) 46 | assert result == 0 47 | -------------------------------------------------------------------------------- /tests/commands/list/test_lmove.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_mylists(redis: Redis): 8 | mylists = ["mylist1", "mylist2", "mylist3", "mylist4", "mylist5", "mylist6"] 9 | 10 | for mylist in mylists: 11 | redis.delete(mylist) 12 | 13 | 14 | def test_lmove_existing_key(redis: Redis): 15 | source = "mylist1" 16 | destination = "mylist2" 17 | values = ["value1", "value2", "value3"] 18 | 19 | redis.rpush(source, *values) 20 | 21 | result = redis.lmove(source, destination, "LEFT") 22 | assert result == "value1" 23 | assert redis.lrange(source, 0, -1) == ["value2", "value3"] 24 | assert redis.lrange(destination, 0, -1) == ["value1"] 25 | 26 | redis.delete(source) 27 | redis.delete(destination) 28 | 29 | 30 | def test_lmove_nonexistent_key(redis: Redis): 31 | source = "mylist2" 32 | destination = "mylist3" 33 | 34 | result = redis.lmove(source, destination, "LEFT") 35 | assert result is None 36 | 37 | redis.delete(destination) 38 | 39 | 40 | def test_lmove_invalid_direction(redis: Redis): 41 | source = "mylist3" 42 | destination = "mylist4" 43 | values = ["value1", "value2", "value3"] 44 | 45 | redis.rpush(source, *values) 46 | 47 | with pytest.raises(Exception): 48 | redis.lmove(source, destination, "INVALID_DIRECTION") # type: ignore[arg-type] 49 | 50 | redis.delete(source) 51 | 52 | 53 | def test_lmove_same_source_and_destination(redis: Redis): 54 | source = "mylist5" 55 | destination = "mylist6" 56 | values = ["value1", "value2", "value3"] 57 | 58 | redis.rpush(source, *values) 59 | 60 | result = redis.lmove(source, destination, "LEFT") 61 | assert result == "value1" 62 | assert redis.lrange(source, 0, -1) == ["value2", "value3"] 63 | assert redis.lrange(destination, 0, -1) == ["value1"] 64 | 65 | redis.delete(source) 66 | redis.delete(destination) 67 | -------------------------------------------------------------------------------- /tests/commands/list/test_lpop.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_mylists(redis: Redis): 8 | mylists = ["mylist1", "mylist2", "mylist3", "mylist4", "mylist5"] 9 | 10 | for mylist in mylists: 11 | redis.delete(mylist) 12 | 13 | 14 | def test_lpop_existing_key(redis: Redis): 15 | mylist = "mylist1" 16 | values = ["value1", "value2", "value3"] 17 | 18 | redis.rpush(mylist, *values) 19 | 20 | result = redis.lpop(mylist) 21 | assert result == "value1" 22 | assert redis.lrange(mylist, 0, -1) == ["value2", "value3"] 23 | 24 | redis.delete(mylist) 25 | 26 | 27 | def test_lpop_empty_list(redis: Redis): 28 | mylist = "mylist2" 29 | 30 | result = redis.lpop(mylist) 31 | assert result is None 32 | 33 | redis.delete(mylist) 34 | 35 | 36 | def test_lpop_nonexistent_key(redis: Redis): 37 | mylist = "mylist3" 38 | 39 | result = redis.lpop(mylist) 40 | assert result is None 41 | 42 | redis.delete(mylist) 43 | 44 | 45 | def test_lpop_with_count(redis: Redis): 46 | mylist = "mylist4" 47 | values = ["value1", "value2", "value3", "value4"] 48 | 49 | redis.rpush(mylist, *values) 50 | 51 | # Pop two elements using count parameter 52 | result = redis.lpop(mylist, count=2) 53 | assert result == ["value1", "value2"] 54 | assert redis.lrange(mylist, 0, -1) == ["value3", "value4"] 55 | 56 | # Pop all remaining elements 57 | result = redis.lpop(mylist, count=2) 58 | assert result == ["value3", "value4"] 59 | assert redis.lrange(mylist, 0, -1) == [] 60 | 61 | redis.delete(mylist) 62 | 63 | 64 | def test_lpop_with_count_zero(redis: Redis): 65 | mylist = "mylist5" 66 | values = ["value1", "value2", "value3", "value4"] 67 | 68 | redis.rpush(mylist, *values) 69 | 70 | # Pop zero elements 71 | result = redis.lpop(mylist, count=0) 72 | assert result == [] 73 | assert redis.lrange(mylist, 0, -1) == ["value1", "value2", "value3", "value4"] 74 | 75 | redis.delete(mylist) 76 | -------------------------------------------------------------------------------- /tests/commands/list/test_lpos.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_mylists(redis: Redis): 8 | mylists = ["mylist1", "mylist2", "mylist3", "mylist4", "mylist5"] 9 | 10 | for mylist in mylists: 11 | redis.delete(mylist) 12 | 13 | 14 | def test_lpos_existing_value(redis: Redis): 15 | mylist = "mylist1" 16 | values = ["value1", "value2", "value3"] 17 | 18 | redis.rpush(mylist, *values) 19 | 20 | result = redis.lpos(mylist, "value2") 21 | assert result == 1 22 | 23 | 24 | def test_lpos_nonexistent_value(redis: Redis): 25 | mylist = "mylist2" 26 | values = ["value1", "value2", "value3"] 27 | 28 | redis.rpush(mylist, *values) 29 | 30 | result = redis.lpos(mylist, "value4") 31 | assert result is None 32 | 33 | 34 | def test_lpos_with_count(redis: Redis): 35 | mylist = "mylist3" 36 | values = ["value1", "value2", "value2", "value3", "value2"] 37 | 38 | redis.rpush(mylist, *values) 39 | 40 | result = redis.lpos(mylist, "value2", count=2) 41 | assert result == [1, 2] 42 | 43 | 44 | def test_lpos_with_rank(redis: Redis): 45 | mylist = "mylist4" 46 | values = ["value1", "value2", "value3", "value2"] 47 | 48 | redis.rpush(mylist, *values) 49 | 50 | result = redis.lpos(mylist, "value2", rank=2) 51 | assert result == 3 52 | 53 | 54 | def test_lpos_with_maxlen(redis: Redis): 55 | mylist = "mylist5" 56 | values = ["value1", "value2", "value3", "value4"] 57 | 58 | redis.rpush(mylist, *values) 59 | 60 | result = redis.lpos(mylist, "value2", maxlen=3) 61 | assert result == 1 62 | -------------------------------------------------------------------------------- /tests/commands/list/test_lpush.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | import pytest 4 | 5 | from upstash_redis import Redis 6 | 7 | 8 | @pytest.fixture(autouse=True) 9 | def flush_mylists(redis: Redis): 10 | mylists = ["mylist1", "mylist2", "mylist3", "mylist4"] 11 | 12 | for mylist in mylists: 13 | redis.delete(mylist) 14 | 15 | 16 | def test_lpush_single_value(redis: Redis): 17 | mylist = "mylist1" 18 | value = "value1" 19 | 20 | result = redis.lpush(mylist, value) 21 | assert result == 1 22 | assert redis.lrange(mylist, 0, -1) == [value] 23 | 24 | 25 | def test_lpush_multiple_values(redis: Redis): 26 | mylist = "mylist2" 27 | values = ["value1", "value2", "value3"] 28 | reverse_values = ["value3", "value2", "value1"] 29 | 30 | result = redis.lpush(mylist, *values) 31 | assert result == len(values) 32 | assert redis.lrange(mylist, 0, -1) == reverse_values 33 | 34 | 35 | def test_lpush_existing_list(redis: Redis): 36 | mylist = "mylist3" 37 | values = ["value1", "value2"] 38 | 39 | redis.lpush(mylist, *values) 40 | 41 | new_values = ["value3", "value4"] 42 | result = redis.lpush(mylist, *new_values) 43 | assert result == len(new_values) + len(values) 44 | assert redis.lrange(mylist, 0, -1) == ["value4", "value3", "value2", "value1"] 45 | 46 | 47 | def test_lpush_empty_list(redis: Redis): 48 | mylist = "mylist4" 49 | values: List = [] 50 | 51 | with pytest.raises(Exception): 52 | redis.lpush(mylist, *values) 53 | -------------------------------------------------------------------------------- /tests/commands/list/test_lpushx.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_mylists(redis: Redis): 8 | mylists = ["mylist1", "mylist2"] 9 | 10 | for mylist in mylists: 11 | redis.delete(mylist) 12 | 13 | 14 | def test_lpushx_existing_list(redis: Redis): 15 | mylist = "mylist1" 16 | values = ["value1", "value2"] 17 | reversed_values = ["value2", "value1"] 18 | 19 | # Create an initial list 20 | redis.lpush(mylist, *values) 21 | 22 | # Perform LPUSHX on the existing list 23 | new_value = "new_value" 24 | result = redis.lpushx(mylist, new_value) 25 | assert result == 3 # The length of the list after LPUSHX 26 | 27 | # Verify that the new value is added to the list 28 | expected_list = [new_value] + reversed_values 29 | assert redis.lrange(mylist, 0, -1) == expected_list 30 | 31 | 32 | def test_lpushx_nonexistent_list(redis: Redis): 33 | mylist = "mylist2" 34 | value = "value1" 35 | 36 | # Perform LPUSHX on a nonexistent list 37 | result = redis.lpushx(mylist, value) 38 | assert result == 0 # LPUSHX should have no effect 39 | 40 | # Verify that the list remains empty 41 | assert redis.lrange(mylist, 0, -1) == [] 42 | -------------------------------------------------------------------------------- /tests/commands/list/test_lrange.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_mylist(redis: Redis): 8 | mylist = "mylist" 9 | 10 | redis.delete(mylist) 11 | 12 | 13 | def test_lrange_full_range(redis: Redis): 14 | mylist = "mylist" 15 | values = ["value1", "value2", "value3", "value4"] 16 | 17 | redis.rpush(mylist, *values) 18 | 19 | result = redis.lrange(mylist, 0, -1) 20 | assert result == values 21 | 22 | 23 | def test_lrange_partial_range(redis: Redis): 24 | mylist = "mylist" 25 | values = ["value1", "value2", "value3", "value4"] 26 | 27 | redis.rpush(mylist, *values) 28 | 29 | result = redis.lrange(mylist, 1, 2) 30 | assert result == values[1:3] 31 | 32 | 33 | def test_lrange_out_of_range_start(redis: Redis): 34 | mylist = "mylist" 35 | values = ["value1", "value2", "value3", "value4"] 36 | 37 | redis.rpush(mylist, *values) 38 | 39 | result = redis.lrange(mylist, 10, 15) 40 | assert result == [] 41 | 42 | 43 | def test_lrange_out_of_range_end(redis: Redis): 44 | mylist = "mylist" 45 | values = ["value1", "value2", "value3", "value4"] 46 | 47 | redis.rpush(mylist, *values) 48 | 49 | result = redis.lrange(mylist, 1, 10) 50 | assert result == values[1:] 51 | 52 | 53 | def test_lrange_empty_list(redis: Redis): 54 | mylist = "mylist" 55 | 56 | result = redis.lrange(mylist, 0, -1) 57 | assert result == [] 58 | -------------------------------------------------------------------------------- /tests/commands/list/test_lrem.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_mylist(redis: Redis): 8 | mylist = "mylist" 9 | 10 | redis.delete(mylist) 11 | 12 | 13 | def test_lrem_existing_value(redis: Redis): 14 | mylist = "mylist" 15 | values = ["value1", "value2", "value3", "value2"] 16 | 17 | redis.rpush(mylist, *values) 18 | 19 | result = redis.lrem(mylist, count=0, element="value2") 20 | assert result == 2 # Number of removed elements 21 | 22 | expected_list = ["value1", "value3"] 23 | assert redis.lrange(mylist, 0, -1) == expected_list 24 | 25 | 26 | def test_lrem_nonexistent_value(redis: Redis): 27 | mylist = "mylist" 28 | values = ["value1", "value2", "value3"] 29 | 30 | redis.rpush(mylist, *values) 31 | 32 | result = redis.lrem(mylist, count=0, element="value4") 33 | assert result == 0 # No elements removed 34 | 35 | assert redis.lrange(mylist, 0, -1) == values 36 | 37 | 38 | def test_lrem_with_count(redis: Redis): 39 | mylist = "mylist" 40 | values = ["value1", "value2", "value3", "value2"] 41 | 42 | redis.rpush(mylist, *values) 43 | 44 | result = redis.lrem(mylist, count=1, element="value2") 45 | assert result == 1 # Number of removed elements 46 | 47 | expected_list = ["value1", "value3", "value2"] 48 | assert redis.lrange(mylist, 0, -1) == expected_list 49 | -------------------------------------------------------------------------------- /tests/commands/list/test_lset.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_mylist(redis: Redis): 8 | mylist = "mylist" 9 | 10 | redis.delete(mylist) 11 | 12 | 13 | def test_lset_existing_index(redis: Redis): 14 | mylist = "mylist" 15 | values = ["value1", "value2", "value3"] 16 | 17 | redis.rpush(mylist, *values) 18 | 19 | redis.lset(mylist, 1, "new_value") 20 | 21 | expected_list = ["value1", "new_value", "value3"] 22 | assert redis.lrange(mylist, 0, -1) == expected_list 23 | 24 | 25 | def test_lset_nonexistent_index(redis: Redis): 26 | mylist = "mylist" 27 | values = ["value1", "value2", "value3"] 28 | 29 | redis.rpush(mylist, *values) 30 | 31 | with pytest.raises(Exception): 32 | redis.lset(mylist, 3, "new_value") 33 | 34 | assert redis.lrange(mylist, 0, -1) == values 35 | -------------------------------------------------------------------------------- /tests/commands/list/test_ltrim.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_mylist(redis: Redis): 8 | mylist = "mylist" 9 | 10 | redis.delete(mylist) 11 | 12 | 13 | def test_ltrim_with_positive_indices(redis: Redis): 14 | mylist = "mylist" 15 | values = ["value1", "value2", "value3", "value4", "value5"] 16 | 17 | redis.rpush(mylist, *values) 18 | 19 | redis.ltrim(mylist, 1, 3) 20 | 21 | expected_list = ["value2", "value3", "value4"] 22 | assert redis.lrange(mylist, 0, -1) == expected_list 23 | 24 | 25 | def test_ltrim_with_negative_indices(redis: Redis): 26 | mylist = "mylist" 27 | values = ["value1", "value2", "value3", "value4", "value5"] 28 | 29 | redis.rpush(mylist, *values) 30 | 31 | redis.ltrim(mylist, -3, -2) 32 | 33 | expected_list = ["value3", "value4"] 34 | assert redis.lrange(mylist, 0, -1) == expected_list 35 | -------------------------------------------------------------------------------- /tests/commands/list/test_rpop.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_mylist(request, redis: Redis): 8 | mylist = f"mylist_{request.node.name}" 9 | redis.delete(mylist) 10 | yield 11 | redis.delete(mylist) 12 | 13 | 14 | def test_rpop_existing_element(redis: Redis): 15 | mylist = "mylist_existing" 16 | values = ["value1", "value2", "value3"] 17 | 18 | redis.rpush(mylist, *values) 19 | 20 | result = redis.rpop(mylist) 21 | assert result == "value3" 22 | 23 | expected_list = ["value1", "value2"] 24 | assert redis.lrange(mylist, 0, -1) == expected_list 25 | 26 | 27 | def test_rpop_empty_list(redis: Redis): 28 | mylist = "mylist_empty" 29 | 30 | result = redis.rpop(mylist) 31 | assert result is None 32 | 33 | 34 | def test_rpop_nonexistent_list(redis: Redis): 35 | mylist = "nonexistent_list" 36 | 37 | assert redis.rpop(mylist) is None 38 | -------------------------------------------------------------------------------- /tests/commands/list/test_rpoplpush.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_lists(redis: Redis): 8 | lists = ["list1", "list2", "list3"] 9 | 10 | for list_name in lists: 11 | redis.delete(list_name) 12 | 13 | yield 14 | 15 | for list_name in lists: 16 | redis.delete(list_name) 17 | 18 | 19 | def test_rpoplpush_existing_elements(redis: Redis): 20 | source_list = "list1" 21 | destination_list = "list2" 22 | values = ["value1", "value2", "value3"] 23 | 24 | redis.rpush(source_list, *values) 25 | 26 | result = redis.rpoplpush(source_list, destination_list) 27 | assert result == "value3" 28 | 29 | expected_source_list = ["value1", "value2"] 30 | assert redis.lrange(source_list, 0, -1) == expected_source_list 31 | 32 | expected_destination_list = ["value3"] 33 | assert redis.lrange(destination_list, 0, -1) == expected_destination_list 34 | 35 | 36 | def test_rpoplpush_empty_source_list(redis: Redis): 37 | source_list = "list2" 38 | destination_list = "list3" 39 | 40 | result = redis.rpoplpush(source_list, destination_list) 41 | assert result is None 42 | 43 | assert redis.llen(source_list) == 0 44 | assert redis.llen(destination_list) == 0 45 | 46 | 47 | def test_rpoplpush_nonexistent_lists(redis: Redis): 48 | source_list = "nonexistent_list1" 49 | destination_list = "nonexistent_list2" 50 | 51 | assert redis.rpoplpush(source_list, destination_list) is None 52 | -------------------------------------------------------------------------------- /tests/commands/list/test_rpush.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_lists(redis: Redis): 8 | lists = ["list1", "list2", "list3", "nonexistent_list"] 9 | 10 | for list_name in lists: 11 | redis.delete(list_name) 12 | 13 | yield 14 | 15 | for list_name in lists: 16 | redis.delete(list_name) 17 | 18 | 19 | def test_rpush_existing_list(redis: Redis): 20 | mylist = "list1" 21 | values = ["value1", "value2", "value3"] 22 | 23 | result = redis.rpush(mylist, *values) 24 | assert result == 3 25 | 26 | expected_list = ["value1", "value2", "value3"] 27 | assert redis.lrange(mylist, 0, -1) == expected_list 28 | 29 | 30 | def test_rpush_empty_list(redis: Redis): 31 | mylist = "list2" 32 | 33 | with pytest.raises(Exception): 34 | redis.rpush(mylist) 35 | 36 | 37 | def test_rpush_nonexistent_list(redis: Redis): 38 | mylist = "nonexistent_list" 39 | 40 | result = redis.rpush(mylist, "value1", "value2") 41 | assert result == 2 42 | 43 | expected_list = ["value1", "value2"] 44 | assert redis.lrange(mylist, 0, -1) == expected_list 45 | -------------------------------------------------------------------------------- /tests/commands/list/test_rpushx.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_lists(redis: Redis): 8 | lists = ["list1", "list2", "list3"] 9 | 10 | for list_name in lists: 11 | redis.delete(list_name) 12 | 13 | yield 14 | 15 | for list_name in lists: 16 | redis.delete(list_name) 17 | 18 | 19 | def test_rpushx_existing_list(redis: Redis): 20 | mylist = "list1" 21 | values = ["value1", "value2", "value3"] 22 | 23 | result = redis.rpush(mylist, *values) 24 | assert result == 3 25 | 26 | new_values = ["value4", "value5"] 27 | 28 | result = redis.rpushx(mylist, *new_values) 29 | assert result == 5 30 | 31 | expected_list = ["value1", "value2", "value3", "value4", "value5"] 32 | assert redis.lrange(mylist, 0, -1) == expected_list 33 | 34 | 35 | def test_rpushx_empty_list(redis: Redis): 36 | mylist = "list2" 37 | values = ["value1", "value2", "value3"] 38 | 39 | result = redis.rpushx(mylist, *values) 40 | assert result == 0 41 | 42 | assert redis.llen(mylist) == 0 43 | 44 | 45 | def test_rpushx_nonexistent_list(redis: Redis): 46 | mylist = "nonexistent_list" 47 | values = ["value1", "value2"] 48 | 49 | result = redis.rpushx(mylist, *values) 50 | assert result == 0 51 | 52 | assert redis.llen(mylist) == 0 53 | -------------------------------------------------------------------------------- /tests/commands/server/test_dbsize.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_data(redis: Redis): 8 | redis.flushdb() 9 | 10 | 11 | def test_dbsize_empty(redis: Redis): 12 | result = redis.dbsize() 13 | assert result == 0 14 | 15 | 16 | def test_dbsize_nonempty(redis: Redis): 17 | redis.set("key1", "value1") 18 | redis.set("key2", "value2") 19 | redis.set("key3", "value3") 20 | 21 | result = redis.dbsize() 22 | assert result == 3 23 | 24 | 25 | def test_dbsize_after_deletion(redis: Redis): 26 | redis.set("key1", "value1") 27 | redis.set("key2", "value2") 28 | redis.set("key3", "value3") 29 | 30 | redis.delete("key2") 31 | 32 | result = redis.dbsize() 33 | assert result == 2 34 | -------------------------------------------------------------------------------- /tests/commands/server/test_flushall.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_all(redis: Redis): 8 | redis.flushall() 9 | 10 | 11 | def test_flushall(redis: Redis): 12 | redis.set("key1", "value1") 13 | redis.set("key2", "value2") 14 | redis.set("key3", "value3") 15 | 16 | result = redis.flushall() 17 | assert result is True 18 | 19 | assert redis.get("key1") is None 20 | assert redis.get("key2") is None 21 | assert redis.get("key3") is None 22 | -------------------------------------------------------------------------------- /tests/commands/server/test_flushdb.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_db(redis: Redis): 8 | redis.flushdb() 9 | 10 | 11 | def test_flushdb(redis: Redis): 12 | redis.set("key1", "value1") 13 | redis.set("key2", "value2") 14 | redis.set("key3", "value3") 15 | 16 | result = redis.flushdb() 17 | assert result is True 18 | 19 | assert redis.get("key1") is None 20 | assert redis.get("key2") is None 21 | assert redis.get("key3") is None 22 | -------------------------------------------------------------------------------- /tests/commands/server/test_time.py: -------------------------------------------------------------------------------- 1 | from upstash_redis import Redis 2 | 3 | 4 | def test_time(redis: Redis): 5 | result = redis.time() 6 | assert isinstance(result, tuple) 7 | assert len(result) == 2 8 | assert isinstance(result[0], int) 9 | assert isinstance(result[1], int) 10 | -------------------------------------------------------------------------------- /tests/commands/set/test_sadd.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_set(redis: Redis): 8 | set_name = "myset" 9 | redis.delete(set_name) 10 | 11 | 12 | def test_sadd(redis: Redis): 13 | set_name = "myset" 14 | 15 | # Add elements to the set 16 | result = redis.sadd(set_name, "element1", "element2", "element3") 17 | 18 | assert result == 3 # Number of elements added to the set 19 | 20 | # Verify that the set contains the added elements 21 | members = redis.smembers(set_name) 22 | members.sort() 23 | assert members == ["element1", "element2", "element3"] 24 | -------------------------------------------------------------------------------- /tests/commands/set/test_scard.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_set(redis: Redis): 8 | set_name = "myset" 9 | redis.delete(set_name) 10 | yield 11 | redis.delete(set_name) 12 | 13 | 14 | def test_scard(redis: Redis): 15 | set_name = "myset" 16 | 17 | # Add elements to the set 18 | redis.sadd(set_name, "element1", "element3") 19 | 20 | # Get the cardinality of the set 21 | result = redis.scard(set_name) 22 | 23 | assert result == 2 24 | -------------------------------------------------------------------------------- /tests/commands/set/test_sdiff.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sets(redis: Redis): 8 | set1 = "set1" 9 | set2 = "set2" 10 | set3 = "set3" 11 | 12 | redis.delete(set1, set2, set3) 13 | 14 | 15 | def test_sdiff(redis: Redis): 16 | set1 = "set1" 17 | set2 = "set2" 18 | set3 = "set3" 19 | 20 | # Add elements to the sets 21 | redis.sadd(set1, "element1", "element2", "element3", "element5", "element6") 22 | redis.sadd(set2, "element2", "element3", "element4") 23 | redis.sadd(set3, "element3", "element4", "element5") 24 | 25 | result = redis.sdiff(set1, set2, set3) 26 | 27 | result.sort() 28 | assert result == ["element1", "element6"] 29 | -------------------------------------------------------------------------------- /tests/commands/set/test_sdiffstore.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sets(redis: Redis): 8 | set1 = "set1" 9 | set2 = "set2" 10 | set3 = "set3" 11 | destination_set = "diff_set" 12 | 13 | redis.delete(set1, set2, set3, destination_set) 14 | 15 | 16 | def test_sdiffstore(redis: Redis): 17 | set1 = "set1" 18 | set2 = "set2" 19 | set3 = "set3" 20 | destination_set = "diff_set" 21 | 22 | # Add elements to the sets 23 | redis.sadd(set1, "element1", "element2", "element3") 24 | redis.sadd(set2, "element2", "element3", "element4") 25 | redis.sadd(set3, "element3", "element4", "element5") 26 | 27 | # Compute and store the difference of sets in a destination set 28 | result = redis.sdiffstore(destination_set, set1, set2, set3) 29 | 30 | expected_result = 1 # Expected number of elements in the destination set 31 | assert result == expected_result 32 | 33 | assert redis.smembers(destination_set) == ["element1"] 34 | -------------------------------------------------------------------------------- /tests/commands/set/test_sinter.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sets(redis: Redis): 8 | set1 = "set1" 9 | set2 = "set2" 10 | set3 = "set3" 11 | 12 | redis.delete(set1, set2, set3) 13 | 14 | 15 | def test_sinter(redis: Redis): 16 | set1 = "set1" 17 | set2 = "set2" 18 | set3 = "set3" 19 | 20 | # Add elements to the sets 21 | redis.sadd(set1, "element1", "element2", "element3") 22 | redis.sadd(set2, "element2", "element3", "element4") 23 | redis.sadd(set3, "element3", "element4", "element5") 24 | 25 | # Compute the intersection of sets 26 | result = redis.sinter(set1, set2, set3) 27 | 28 | assert result == ["element3"] 29 | -------------------------------------------------------------------------------- /tests/commands/set/test_sinterstore.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sets(redis: Redis): 8 | set1 = "set1" 9 | set2 = "set2" 10 | set3 = "set3" 11 | result_set = "result_set" 12 | 13 | redis.delete(set1, set2, set3, result_set) 14 | 15 | 16 | def test_sinterstore(redis: Redis): 17 | set1 = "set1" 18 | set2 = "set2" 19 | set3 = "set3" 20 | result_set = "result_set" 21 | 22 | # Add elements to the sets 23 | redis.sadd(set1, "element1", "element2", "element3") 24 | redis.sadd(set2, "element2", "element3", "element4") 25 | redis.sadd(set3, "element3", "element4", "element5") 26 | 27 | # Compute the intersection of sets and store the result in a new set 28 | result = redis.sinterstore(result_set, set1, set2, set3) 29 | 30 | expected_result = 1 # Number of elements in the resulting set 31 | assert result == expected_result 32 | 33 | assert redis.smembers(result_set) == ["element3"] 34 | -------------------------------------------------------------------------------- /tests/commands/set/test_sismember.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_set(redis: Redis): 8 | set_name = "myset" 9 | redis.delete(set_name) 10 | yield 11 | redis.delete(set_name) 12 | 13 | 14 | def test_sismember(redis: Redis): 15 | set_name = "myset" 16 | 17 | # Add elements to the set 18 | redis.sadd(set_name, "element1", "element2", "element3") 19 | 20 | # Check if elements are members of the set 21 | assert redis.sismember(set_name, "element1") is True 22 | assert redis.sismember(set_name, "element2") == 1 23 | assert redis.sismember(set_name, "element3") == 1 24 | assert redis.sismember(set_name, "element4") == 0 25 | -------------------------------------------------------------------------------- /tests/commands/set/test_smembers.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_set(redis: Redis): 8 | set_name = "myset" 9 | 10 | redis.delete(set_name) 11 | 12 | 13 | def test_smembers(redis: Redis): 14 | set_name = "myset" 15 | 16 | # Add elements to the set 17 | redis.sadd(set_name, "element1", "element2", "element3") 18 | 19 | # Get all members of the set 20 | members = redis.smembers(set_name) 21 | 22 | # Assert that the members are correct 23 | members.sort() 24 | assert members == ["element1", "element2", "element3"] 25 | -------------------------------------------------------------------------------- /tests/commands/set/test_smismember.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_set(redis: Redis): 8 | set_name = "myset" 9 | redis.delete(set_name) 10 | yield 11 | redis.delete(set_name) 12 | 13 | 14 | def test_smismember(redis: Redis): 15 | set_name = "myset" 16 | 17 | redis.sadd(set_name, "element1", "element2", "element3") 18 | 19 | members = redis.smismember(set_name, "element1", "element3", "non_existing_element") 20 | 21 | assert isinstance(members, list) 22 | 23 | assert members == [True, 1, False] 24 | 25 | 26 | def test_smismember_nonexisting_set(redis: Redis): 27 | members = redis.smismember( 28 | "non_existing_set", "element1", "element3", "non_existing_element" 29 | ) 30 | 31 | assert members == [False, False, False] 32 | -------------------------------------------------------------------------------- /tests/commands/set/test_smove.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sets(redis: Redis): 8 | set_name1 = "set1" 9 | set_name2 = "set2" 10 | redis.delete(set_name1) 11 | redis.delete(set_name2) 12 | yield 13 | redis.delete(set_name1) 14 | redis.delete(set_name2) 15 | 16 | 17 | def test_smove_existing_member(redis: Redis): 18 | set_name1 = "set1" 19 | set_name2 = "set2" 20 | 21 | # Add a member to set1 22 | redis.sadd(set_name1, "member1") 23 | 24 | # Move the member from set1 to set2 25 | result = redis.smove(set_name1, set_name2, "member1") 26 | 27 | # Assert that the move operation was successful 28 | assert result == 1 29 | 30 | # Assert that the member has been moved to set2 31 | assert redis.sismember(set_name2, "member1") 32 | assert not redis.sismember(set_name1, "member1") 33 | 34 | 35 | def test_smove_nonexistent_member(redis: Redis): 36 | set_name1 = "set1" 37 | set_name2 = "set2" 38 | 39 | # Attempt to move a non-existent member from set1 to set2 40 | result = redis.smove(set_name1, set_name2, "nonexistent") 41 | 42 | # Assert that the move operation returns 0, indicating the member doesn't exist 43 | assert result == 0 44 | 45 | # Assert that both sets remain empty 46 | assert redis.scard(set_name1) == 0 47 | assert redis.scard(set_name2) == 0 48 | -------------------------------------------------------------------------------- /tests/commands/set/test_spop.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_set(redis: Redis): 8 | set_name = "myset" 9 | redis.delete(set_name) 10 | yield 11 | redis.delete(set_name) 12 | 13 | 14 | def test_spop_existing_member(redis: Redis): 15 | set_name = "myset" 16 | 17 | # Add members to the set 18 | redis.sadd(set_name, "member1", "member2", "member3") 19 | 20 | # Perform the spop operation 21 | result = redis.spop(set_name) 22 | 23 | # Assert that the result is not None 24 | assert result is not None 25 | assert isinstance(result, str) 26 | 27 | # Assert that the popped member is not present in the set anymore 28 | assert not redis.sismember(set_name, result) 29 | 30 | 31 | def test_spop_empty_set(redis: Redis): 32 | set_name = "myset" 33 | 34 | # Perform the spop operation on an empty set 35 | result = redis.spop(set_name) 36 | 37 | # Assert that the result is None 38 | assert result is None 39 | 40 | 41 | def test_spop_nonexistent_set(redis: Redis): 42 | set_name = "nonexistent_set" 43 | 44 | # Perform the spop operation on a nonexistent set 45 | result = redis.spop(set_name) 46 | 47 | # Assert that the result is None 48 | assert result is None 49 | -------------------------------------------------------------------------------- /tests/commands/set/test_srandmember.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_set(redis: Redis): 8 | set_name = "myset" 9 | 10 | redis.delete(set_name) 11 | 12 | 13 | def test_srandmember_existing_set(redis: Redis): 14 | set_name = "myset" 15 | members = ["member1", "member2", "member3"] 16 | 17 | # Add members to the set 18 | redis.sadd(set_name, *members) 19 | 20 | # Perform the srandmember operation 21 | result = redis.srandmember(set_name) 22 | 23 | # Assert that the result is a member of the set 24 | assert result in members 25 | 26 | 27 | def test_srandmember_nonexistent_set(redis: Redis): 28 | set_name = "nonexistent_set" 29 | 30 | # Perform the srandmember operation on a nonexistent set 31 | result = redis.srandmember(set_name) 32 | 33 | # Assert that the result is None 34 | assert result is None 35 | -------------------------------------------------------------------------------- /tests/commands/set/test_srem.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_set(redis: Redis): 8 | set_name = "myset" 9 | redis.delete(set_name) 10 | yield 11 | redis.delete(set_name) 12 | 13 | 14 | def test_srem_existing_members(redis: Redis): 15 | set_name = "myset" 16 | members = ["member1", "member2", "member3"] 17 | 18 | # Add members to the set 19 | redis.sadd(set_name, *members) 20 | 21 | # Perform the srem operation 22 | result = redis.srem(set_name, "member1", "member3") 23 | 24 | # Assert that the result is the number of members removed 25 | assert result == 2 26 | 27 | # Assert that the removed members no longer exist in the set 28 | assert redis.sismember(set_name, "member1") == 0 29 | assert redis.sismember(set_name, "member3") is False 30 | 31 | 32 | def test_srem_nonexistent_members(redis: Redis): 33 | set_name = "myset" 34 | members = ["member1", "member2", "member3"] 35 | 36 | # Add members to the set 37 | redis.sadd(set_name, *members) 38 | 39 | # Perform the srem operation with non-existent members 40 | result = redis.srem(set_name, "member4", "member5") 41 | 42 | # Assert that the result is 0, as no members were removed 43 | assert result == 0 44 | 45 | # Assert that the original members still exist in the set 46 | assert redis.sismember(set_name, "member1") == 1 47 | assert redis.sismember(set_name, "member2") is True 48 | assert redis.sismember(set_name, "member3") is True 49 | -------------------------------------------------------------------------------- /tests/commands/set/test_sscan.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_set(redis: Redis): 8 | set_name = "myset" 9 | 10 | redis.delete(set_name) 11 | 12 | 13 | def test_sscan_with_match_and_count(redis: Redis): 14 | set_name = "myset" 15 | 16 | # Add members to the set 17 | members = ["apple", "banana", "cherry", "avocado", "apricot"] 18 | redis.sadd(set_name, *members) 19 | 20 | # Use SSCAN to retrieve members matching a pattern with a count of 2 21 | cursor, matching_members = redis.sscan(set_name, match="a*", count=2) 22 | 23 | # Assert that the matching members are returned with the specified count 24 | assert all(member in members for member in matching_members) 25 | 26 | 27 | def test_sscan_without_match_and_count(redis: Redis): 28 | set_name = "myset" 29 | 30 | # Add members to the set 31 | members = ["apple", "banana", "cherry", "avocado", "apricot"] 32 | redis.sadd(set_name, *members) 33 | 34 | # Use SSCAN to retrieve members with a count of 3 35 | cursor, scanned_members = redis.sscan(set_name, count=5) 36 | 37 | # Assert that the specified count of members is returned 38 | assert len(scanned_members) == 5 39 | -------------------------------------------------------------------------------- /tests/commands/set/test_sunion.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sets(redis: Redis): 8 | set1 = "set1" 9 | set2 = "set2" 10 | 11 | redis.delete(set1) 12 | redis.delete(set2) 13 | 14 | 15 | def test_sunion(redis: Redis): 16 | set1 = "set1" 17 | set2 = "set2" 18 | 19 | # Add members to set1 20 | redis.sadd(set1, "apple", "banana", "cherry") 21 | 22 | # Add members to set2 23 | redis.sadd(set2, "banana", "cherry", "date") 24 | 25 | # Perform SUNION operation on set1 and set2 26 | result = redis.sunion(set1, set2) 27 | 28 | # Assert that the union of both sets is returned 29 | result.sort() 30 | assert result == ["apple", "banana", "cherry", "date"] 31 | 32 | 33 | def test_sunion_empty_sets(redis: Redis): 34 | set1 = "set1" 35 | set2 = "set2" 36 | 37 | # Perform SUNION operation on empty set1 and set2 38 | result = redis.sunion(set1, set2) 39 | 40 | # Assert that an empty set is returned 41 | assert result == [] 42 | 43 | 44 | def test_sunion_single_set(redis: Redis): 45 | set1 = "set1" 46 | 47 | # Add members to set1 48 | redis.sadd(set1, "apple", "banana", "cherry") 49 | 50 | # Perform SUNION operation on set1 only 51 | result = redis.sunion(set1) 52 | 53 | # Assert that set1 itself is returned 54 | result.sort() 55 | assert result == ["apple", "banana", "cherry"] 56 | 57 | with pytest.raises(Exception): 58 | redis.sunion() 59 | -------------------------------------------------------------------------------- /tests/commands/set/test_sunionstore.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sets(redis: Redis): 8 | set1 = "set1" 9 | set2 = "set2" 10 | union_set = "union_set" 11 | 12 | redis.delete(set1) 13 | redis.delete(set2) 14 | redis.delete(union_set) 15 | yield 16 | redis.delete(set1) 17 | redis.delete(set2) 18 | redis.delete(union_set) 19 | 20 | 21 | def test_sunionstore(redis: Redis): 22 | set1 = "set1" 23 | set2 = "set2" 24 | union_set = "union_set" 25 | 26 | # Add members to set1 27 | redis.sadd(set1, "apple", "banana", "cherry") 28 | 29 | # Add members to set2 30 | redis.sadd(set2, "banana", "cherry", "date") 31 | 32 | # Perform SUNIONSTORE operation on set1 and set2, storing the result in union_set 33 | result = redis.sunionstore(union_set, set1, set2) 34 | 35 | # Assert that the result is the number of members in the union set 36 | assert result == 4 37 | 38 | # Assert that the members in the union set are as expected 39 | members = redis.smembers(union_set) 40 | members.sort() 41 | assert members == ["apple", "banana", "cherry", "date"] 42 | 43 | 44 | def test_sunionstore_empty_sets(redis: Redis): 45 | set1 = "set1" 46 | set2 = "set2" 47 | union_set = "union_set" 48 | 49 | # Perform SUNIONSTORE operation on empty set1 and set2, storing the result in union_set 50 | result = redis.sunionstore(union_set, set1, set2) 51 | 52 | # Assert that the result is 0 since both sets are empty 53 | assert result == 0 54 | 55 | # Assert that the union set is also empty 56 | assert redis.smembers(union_set) == [] 57 | 58 | 59 | def test_sunionstore_single_set(redis: Redis): 60 | set1 = "set1" 61 | union_set = "union_set" 62 | 63 | # Add members to set1 64 | redis.sadd(set1, "apple", "banana", "cherry") 65 | 66 | # Perform SUNIONSTORE operation on set1 only, storing the result in union_set 67 | result = redis.sunionstore(union_set, set1) 68 | 69 | # Assert that the result is the number of members in the set1 itself 70 | assert result == 3 71 | 72 | # Assert that the members in the union set are the same as set1 73 | members = redis.smembers(union_set) 74 | members.sort() 75 | assert members == ["apple", "banana", "cherry"] 76 | -------------------------------------------------------------------------------- /tests/commands/sortedSet/test_zadd.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sorted_set(redis: Redis): 8 | sorted_set = "sorted_set" 9 | redis.delete(sorted_set) 10 | yield 11 | redis.delete(sorted_set) 12 | 13 | 14 | def test_zadd(redis: Redis): 15 | sorted_set = "sorted_set" 16 | 17 | # Add members with their respective scores to the sorted set 18 | result = redis.zadd(sorted_set, {"member1": 10, "member2": 20, "member3": 30}) 19 | 20 | # Assert that the result is the number of members added 21 | assert result == 3 22 | 23 | # Assert that the members are added with their scores 24 | assert redis.zrange(sorted_set, 0, -1, withscores=True) == [ 25 | ("member1", 10.0), 26 | ("member2", 20.0), 27 | ("member3", 30.0), 28 | ] 29 | 30 | 31 | def test_zadd_existing_member(redis: Redis): 32 | sorted_set = "sorted_set" 33 | 34 | # Add an initial member to the sorted set 35 | redis.zadd(sorted_set, {"member1": 10}) 36 | 37 | # Add an existing member with a different score to the sorted set 38 | result = redis.zadd(sorted_set, {"member1": 20}) 39 | 40 | # Assert that the result is 0 since the member already exists 41 | assert result == 0 42 | 43 | # Assert that the score of the existing member is updated 44 | assert redis.zscore(sorted_set, "member1") == 20.0 45 | 46 | 47 | def test_zadd_exception_with_incr(redis: Redis): 48 | sorted_set = "sorted_set" 49 | 50 | # Add multiple members to the sorted set, incr with multiple should throw exception 51 | with pytest.raises(Exception): 52 | redis.zadd(sorted_set, {"member1": 10, "member2": 20}, incr=True) 53 | 54 | 55 | def test_zadd_multiple_members(redis: Redis): 56 | sorted_set = "sorted_set" 57 | 58 | # Add multiple members to the sorted set with their scores 59 | result = redis.zadd(sorted_set, {"member1": 10, "member2": 20, "member3": 30}) 60 | 61 | # Assert that the result is the number of members added 62 | assert result == 3 63 | 64 | result = redis.zadd(sorted_set, {"member1": 5}, incr=True) 65 | assert result == 15 66 | 67 | result = redis.zadd(sorted_set, {"member2": 10}, incr=True) 68 | assert result == 30 69 | 70 | # Assert that the members' scores are incremented correctly 71 | assert redis.zrange(sorted_set, 0, -1, withscores=True) == [ 72 | ("member1", 15.0), 73 | ("member2", 30.0), 74 | ("member3", 30.0), 75 | ] 76 | -------------------------------------------------------------------------------- /tests/commands/sortedSet/test_zcard.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sorted_set(redis: Redis): 8 | sorted_set = "sorted_set" 9 | 10 | redis.delete(sorted_set) 11 | 12 | 13 | def test_zcard(redis: Redis): 14 | sorted_set = "sorted_set" 15 | 16 | # Add members to the sorted set 17 | redis.zadd(sorted_set, {"member1": 10, "member2": 20, "member3": 30}) 18 | 19 | # Get the cardinality of the sorted set 20 | result = redis.zcard(sorted_set) 21 | 22 | # Assert that the result is the correct cardinality 23 | assert result == 3 24 | -------------------------------------------------------------------------------- /tests/commands/sortedSet/test_zcount.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sorted_set(redis: Redis): 8 | sorted_set = "sorted_set" 9 | redis.delete(sorted_set) 10 | yield 11 | redis.delete(sorted_set) 12 | 13 | 14 | def test_zcount(redis: Redis): 15 | sorted_set = "sorted_set" 16 | 17 | # Add members to the sorted set 18 | redis.zadd( 19 | sorted_set, 20 | {"member1": 10, "member2": 20, "member3": 30, "member4": 40, "member5": 50}, 21 | ) 22 | 23 | # Perform a range query using ZCOUNT 24 | result = redis.zcount(sorted_set, min=20, max=40) 25 | 26 | # Assert that the result is the correct count 27 | assert result == 3 28 | 29 | result = redis.zcount(sorted_set, min="-inf", max="+inf") 30 | assert result == 5 31 | -------------------------------------------------------------------------------- /tests/commands/sortedSet/test_zdiff.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sorted_sets(redis: Redis): 8 | sorted_set1 = "sorted_set1" 9 | sorted_set2 = "sorted_set2" 10 | redis.delete(sorted_set1) 11 | redis.delete(sorted_set2) 12 | yield 13 | redis.delete(sorted_set1) 14 | redis.delete(sorted_set2) 15 | 16 | 17 | def test_zdiff(redis: Redis): 18 | sorted_set1 = "sorted_set1" 19 | sorted_set2 = "sorted_set2" 20 | 21 | redis.zadd(sorted_set1, {"member1": 10, "member2": 20, "member3": 30}) 22 | redis.zadd(sorted_set2, {"member2": 20, "member4": 40, "member5": 50}) 23 | 24 | diff_result = redis.zdiff(keys=[sorted_set1, sorted_set2]) 25 | assert diff_result == ["member1", "member3"] 26 | 27 | 28 | def test_zdiff_with_scores(redis: Redis): 29 | sorted_set1 = "sorted_set1" 30 | sorted_set2 = "sorted_set2" 31 | 32 | redis.zadd(sorted_set1, {"member1": 10, "member2": 20, "member3": 30}) 33 | redis.zadd(sorted_set2, {"member2": 20, "member4": 40, "member5": 50}) 34 | 35 | diff_result = redis.zdiff(keys=[sorted_set1, sorted_set2], withscores=True) 36 | assert diff_result == [("member1", 10.0), ("member3", 30.0)] 37 | -------------------------------------------------------------------------------- /tests/commands/sortedSet/test_zdiffstore.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sorted_sets(redis: Redis): 8 | sorted_set1 = "sorted_set1" 9 | sorted_set2 = "sorted_set2" 10 | diff_result = "diff_result" 11 | 12 | redis.delete(sorted_set1) 13 | redis.delete(sorted_set2) 14 | redis.delete(diff_result) 15 | 16 | 17 | def test_zdiffstore(redis: Redis): 18 | sorted_set1 = "sorted_set1" 19 | sorted_set2 = "sorted_set2" 20 | diff_result = "diff_result" 21 | 22 | redis.zadd(sorted_set1, {"member1": 10, "member2": 20, "member3": 30}) 23 | redis.zadd(sorted_set2, {"member2": 20, "member4": 40, "member5": 50}) 24 | 25 | result = redis.zdiffstore(destination=diff_result, keys=[sorted_set1, sorted_set2]) 26 | assert result == 2 27 | 28 | assert redis.zrange(diff_result, 0, -1, withscores=True) == [ 29 | ("member1", 10.0), 30 | ("member3", 30.0), 31 | ] 32 | -------------------------------------------------------------------------------- /tests/commands/sortedSet/test_zincrby.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sorted_set(redis: Redis): 8 | sorted_set = "sorted_set" 9 | redis.delete(sorted_set) 10 | yield 11 | redis.delete(sorted_set) 12 | 13 | 14 | def test_zincrby(redis: Redis): 15 | sorted_set = "sorted_set" 16 | 17 | redis.zadd(sorted_set, {"member1": 10, "member2": 20, "member3": 30}) 18 | 19 | result = redis.zincrby(sorted_set, member="member2", increment=5) 20 | assert result == 25.0 21 | 22 | assert redis.zscore(sorted_set, "member2") == 25.0 23 | 24 | result = redis.zincrby(sorted_set, member="member4", increment=5) 25 | assert result == 5.0 26 | 27 | assert redis.zrange(sorted_set, 0, -1, withscores=True) == [ 28 | ("member4", 5.0), 29 | ("member1", 10.0), 30 | ("member2", 25.0), 31 | ("member3", 30.0), 32 | ] 33 | -------------------------------------------------------------------------------- /tests/commands/sortedSet/test_zinter.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sorted_sets(redis: Redis): 8 | sorted_set1 = "sorted_set1" 9 | sorted_set2 = "sorted_set2" 10 | redis.delete(sorted_set1) 11 | redis.delete(sorted_set2) 12 | yield 13 | redis.delete(sorted_set1) 14 | redis.delete(sorted_set2) 15 | 16 | 17 | def test_zinter(redis: Redis): 18 | sorted_set1 = "sorted_set1" 19 | sorted_set2 = "sorted_set2" 20 | 21 | redis.zadd(sorted_set1, {"member1": 10, "member2": 20, "member3": 30}) 22 | redis.zadd(sorted_set2, {"member1": 5, "member3": 15, "member4": 25}) 23 | 24 | result = redis.zinter(keys=[sorted_set1, sorted_set2]) 25 | 26 | assert result == ["member1", "member3"] 27 | 28 | 29 | def test_zinter_with_scores(redis: Redis): 30 | sorted_set1 = "sorted_set1" 31 | sorted_set2 = "sorted_set2" 32 | 33 | redis.zadd(sorted_set1, {"member1": 10, "member2": 20, "member3": 30}) 34 | redis.zadd(sorted_set2, {"member1": 5, "member3": 15, "member4": 25}) 35 | 36 | result = redis.zinter(keys=[sorted_set1, sorted_set2], withscores=True) 37 | 38 | assert result == [("member1", 15.0), ("member3", 45.0)] 39 | 40 | 41 | def test_zinter_with_aggregate_and_weights(redis: Redis): 42 | sorted_set1 = "sorted_set1" 43 | sorted_set2 = "sorted_set2" 44 | 45 | redis.zadd(sorted_set1, {"member1": 10, "member2": 20, "member3": 30}) 46 | redis.zadd(sorted_set2, {"member1": 5, "member3": 15, "member4": 25}) 47 | 48 | weights = [2, 1] 49 | result = redis.zinter( 50 | keys=[sorted_set1, sorted_set2], 51 | weights=weights, 52 | aggregate="SUM", 53 | withscores=True, 54 | ) 55 | 56 | assert result == [("member1", 25.0), ("member3", 75.0)] 57 | -------------------------------------------------------------------------------- /tests/commands/sortedSet/test_zinterstore.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sorted_sets(redis: Redis): 8 | sorted_set1 = "sorted_set1" 9 | sorted_set2 = "sorted_set2" 10 | destination = "destination" 11 | 12 | redis.delete(sorted_set1) 13 | redis.delete(sorted_set2) 14 | redis.delete(destination) 15 | 16 | 17 | def test_zinterstore(redis: Redis): 18 | sorted_set1 = "sorted_set1" 19 | sorted_set2 = "sorted_set2" 20 | destination = "destination" 21 | 22 | redis.zadd(sorted_set1, {"member1": 10, "member2": 20, "member3": 30, "memberx": 3}) 23 | redis.zadd(sorted_set2, {"member1": 5, "member3": 15, "member4": 25, "memberx": 5}) 24 | 25 | weights = [2, 1] 26 | result = redis.zinterstore( 27 | destination, keys=[sorted_set1, sorted_set2], weights=weights, aggregate="SUM" 28 | ) 29 | assert result == 3 30 | 31 | assert redis.zrange(destination, 0, -1, withscores=True) == [ 32 | ("memberx", 11.0), 33 | ("member1", 25.0), 34 | ("member3", 75.0), 35 | ] 36 | -------------------------------------------------------------------------------- /tests/commands/sortedSet/test_zlexcount.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sorted_set(redis: Redis): 8 | sorted_set = "sorted_set" 9 | 10 | redis.delete(sorted_set) 11 | 12 | 13 | def test_zlexcount(redis: Redis): 14 | sorted_set = "sorted_set" 15 | 16 | redis.zadd( 17 | sorted_set, {"apple": 1, "banana": 2, "cherry": 3, "grape": 4, "orange": 2} 18 | ) 19 | 20 | result = redis.zlexcount(sorted_set, "[banana", "[orange") 21 | assert result == 4 22 | -------------------------------------------------------------------------------- /tests/commands/sortedSet/test_zmscore.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sorted_set(redis: Redis): 8 | sorted_set = "sorted_set" 9 | 10 | redis.delete(sorted_set) 11 | 12 | 13 | def test_zmscore(redis: Redis): 14 | sorted_set = "sorted_set" 15 | 16 | redis.zadd(sorted_set, {"member1": 10, "member2": 20, "member3": 30}) 17 | 18 | members = ["member1", "member3", "non_existing_member"] 19 | result = redis.zmscore(sorted_set, members=members) 20 | 21 | assert result == [10.0, 30.0, None] 22 | -------------------------------------------------------------------------------- /tests/commands/sortedSet/test_zpopmax.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sorted_set(redis: Redis): 8 | sorted_set = "sorted_set" 9 | 10 | redis.delete(sorted_set) 11 | 12 | 13 | def test_zpopmax(redis: Redis): 14 | sorted_set = "sorted_set" 15 | 16 | redis.zadd(sorted_set, {"member1": 10, "member2": 20, "member3": 30}) 17 | assert redis.zscore(sorted_set, "member3") == 30 18 | 19 | result = redis.zpopmax(sorted_set) 20 | assert result == [("member3", 30.0)] 21 | 22 | assert redis.zscore(sorted_set, "member3") is None 23 | 24 | 25 | def test_zpopmax_with_count(redis: Redis): 26 | sorted_set = "sorted_set" 27 | 28 | redis.zadd(sorted_set, {"member1": 10, "member2": 20, "member3": 30}) 29 | 30 | result = redis.zpopmax(sorted_set, count=2) 31 | assert result == [("member3", 30.0), ("member2", 20.0)] 32 | -------------------------------------------------------------------------------- /tests/commands/sortedSet/test_zpopmin.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sorted_set(redis: Redis): 8 | sorted_set = "sorted_set" 9 | 10 | redis.delete(sorted_set) 11 | 12 | 13 | def test_zpopmin(redis: Redis): 14 | sorted_set = "sorted_set" 15 | 16 | redis.zadd(sorted_set, {"member1": 10, "member2": 20, "member3": 30}) 17 | assert redis.zscore(sorted_set, "member1") == 10 18 | 19 | result = redis.zpopmin(sorted_set) 20 | 21 | assert result == [("member1", 10.0)] 22 | 23 | assert redis.zscore(sorted_set, "member1") is None 24 | 25 | 26 | def test_zpopmin_with_count(redis: Redis): 27 | sorted_set = "sorted_set" 28 | 29 | redis.zadd(sorted_set, {"member1": 10, "member2": 20, "member3": 30}) 30 | 31 | result = redis.zpopmin(sorted_set, count=2) 32 | 33 | assert result == [("member1", 10.0), ("member2", 20.0)] 34 | -------------------------------------------------------------------------------- /tests/commands/sortedSet/test_zrandmember.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sorted_set(redis: Redis): 8 | sorted_set = "sorted_set" 9 | 10 | redis.delete(sorted_set) 11 | 12 | 13 | def test_zrandmember(redis: Redis): 14 | sorted_set = "sorted_set" 15 | 16 | redis.zadd(sorted_set, {"member1": 10, "member2": 20, "member3": 30}) 17 | 18 | result = redis.zrandmember(sorted_set) 19 | 20 | assert result in ["member1", "member2", "member3"] 21 | 22 | 23 | def test_zrandmember_with_count(redis: Redis): 24 | sorted_set = "sorted_set" 25 | 26 | redis.zadd(sorted_set, {"member1": 10, "member2": 20, "member3": 30}) 27 | 28 | result = redis.zrandmember(sorted_set, count=2) 29 | 30 | assert isinstance(result, list) 31 | assert len(result) == 2 32 | 33 | assert all(member in ["member1", "member2", "member3"] for member in result) 34 | 35 | 36 | def test_zrandmember_with_withscores(redis: Redis): 37 | sorted_set = "sorted_set" 38 | 39 | redis.zadd(sorted_set, {"member1": 10, "member2": 20, "member3": 30}) 40 | 41 | result = redis.zrandmember(sorted_set, count=2, withscores=True) 42 | assert isinstance(result, list) 43 | 44 | assert all(isinstance(member, tuple) and len(member) == 2 for member in result) 45 | assert all(member[0] in ["member1", "member2", "member3"] for member in result) 46 | assert all(isinstance(member[1], float) for member in result) 47 | -------------------------------------------------------------------------------- /tests/commands/sortedSet/test_zrange.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sorted_set(redis: Redis): 8 | sorted_set = "sorted_set" 9 | 10 | redis.delete(sorted_set) 11 | 12 | 13 | def test_zrange(redis: Redis): 14 | sorted_set = "sorted_set" 15 | 16 | redis.zadd(sorted_set, {"member1": 10, "member2": 20, "member3": 30, "member4": 40}) 17 | 18 | result = redis.zrange(sorted_set, start=1, stop=2, withscores=True, rev=True) 19 | 20 | assert isinstance(result, list) 21 | assert all(isinstance(member, tuple) and len(member) == 2 for member in result) 22 | 23 | assert all(member[0] in ["member3", "member2"] for member in result) 24 | 25 | assert all(isinstance(member[1], float) for member in result) 26 | 27 | 28 | def test_zrange_with_sortby(redis: Redis): 29 | sorted_set = "sorted_set" 30 | 31 | redis.zadd(sorted_set, {"member1": 10, "member2": 20, "member3": 30, "member4": 40}) 32 | 33 | with pytest.raises(Exception): 34 | redis.zrange(sorted_set, start=0, stop=2, withscores=True, sortby="BYLEX") 35 | 36 | result = redis.zrange( 37 | sorted_set, start=0, stop=2, withscores=True, sortby="BYSCORE" 38 | ) 39 | 40 | assert isinstance(result, list) 41 | assert all(isinstance(member, tuple) and len(member) == 2 for member in result) 42 | 43 | assert all(member[0] in ["member1", "member2", "member3"] for member in result) 44 | 45 | assert all(isinstance(member[1], float) for member in result) 46 | 47 | 48 | def test_zrange_with_bylex(redis: Redis): 49 | sorted_set = "sorted_set" 50 | 51 | redis.zadd(sorted_set, {"apple": 1, "banana": 2, "cherry": 3, "date": 4}) 52 | 53 | result = redis.zrange(sorted_set, start="[a", stop="[d", sortby="BYLEX") 54 | assert isinstance(result, list) 55 | 56 | assert set(result) == {"apple", "banana", "cherry"} 57 | -------------------------------------------------------------------------------- /tests/commands/sortedSet/test_zrangebylex.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sorted_set(redis: Redis): 8 | sorted_set = "sorted_set" 9 | 10 | redis.delete(sorted_set) 11 | 12 | 13 | def test_zrangebylex(redis: Redis): 14 | sorted_set = "sorted_set" 15 | 16 | redis.zadd( 17 | sorted_set, {"apple": 1, "banana": 2, "cherry": 3, "mango": 4, "orange": 5} 18 | ) 19 | 20 | result = redis.zrangebylex(sorted_set, min="-", max="(c") 21 | assert result == ["apple", "banana"] 22 | 23 | result = redis.zrangebylex(sorted_set, min="(b", max="+", offset=1, count=2) 24 | assert result == ["cherry", "mango"] 25 | -------------------------------------------------------------------------------- /tests/commands/sortedSet/test_zrangebyscore.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sorted_set(redis: Redis): 8 | sorted_set = "sorted_set" 9 | 10 | redis.delete(sorted_set) 11 | 12 | 13 | def test_zrangebyscore(redis: Redis): 14 | sorted_set = "sorted_set" 15 | 16 | redis.zadd( 17 | sorted_set, {"apple": 10, "banana": 20, "cherry": 30, "mango": 40, "orange": 50} 18 | ) 19 | 20 | result = redis.zrangebyscore(sorted_set, min=20, max=40) 21 | assert result == ["banana", "cherry", "mango"] 22 | 23 | result = redis.zrangebyscore(sorted_set, min=20, max=40, offset=1, count=2) 24 | assert result == ["cherry", "mango"] 25 | 26 | result = redis.zrangebyscore( 27 | sorted_set, 28 | min=20, 29 | max=40, 30 | offset=1, 31 | count=2, 32 | withscores=True, 33 | ) 34 | assert result == [("cherry", 30.0), ("mango", 40.0)] 35 | -------------------------------------------------------------------------------- /tests/commands/sortedSet/test_zrangestore.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sorted_set(redis: Redis): 8 | sorted_set = "sorted_set" 9 | destination = "destination" 10 | 11 | redis.delete(sorted_set) 12 | redis.delete(destination) 13 | 14 | 15 | def test_zrangestore(redis: Redis): 16 | sorted_set = "sorted_set" 17 | destination = "destination" 18 | 19 | redis.zadd(sorted_set, {"member1": 1, "member2": 2, "member3": 3, "member4": 4}) 20 | 21 | result = redis.zrangestore(destination, sorted_set, min=0, max=2) 22 | assert result == 3 23 | 24 | assert redis.zrange(destination, 0, -1, withscores=True) == [ 25 | ("member1", 1.0), 26 | ("member2", 2.0), 27 | ("member3", 3.0), 28 | ] 29 | 30 | 31 | def test_zrangestore_rev(redis: Redis): 32 | sorted_set = "sorted_set" 33 | destination = "destination" 34 | 35 | redis.zadd(sorted_set, {"member1": 1, "member2": 2, "member3": 3, "member4": 4}) 36 | 37 | result = redis.zrangestore(destination, sorted_set, min=0, max=2, rev=True) 38 | assert result == 3 39 | 40 | assert redis.zrange(destination, 0, -1, withscores=True) == [ 41 | ("member2", 2.0), 42 | ("member3", 3.0), 43 | ("member4", 4.0), 44 | ] 45 | 46 | 47 | def test_zrangestore_limit(redis: Redis): 48 | sorted_set = "sorted_set" 49 | destination = "destination" 50 | 51 | redis.zadd(sorted_set, {"member1": 1, "member2": 2, "member3": 3, "member4": 4}) 52 | 53 | result = redis.zrangestore(destination, sorted_set, min=0, max=2, offset=1, count=1) 54 | assert result == 1 55 | 56 | assert redis.zrange(destination, 0, -1, withscores=True) == [("member2", 2.0)] 57 | -------------------------------------------------------------------------------- /tests/commands/sortedSet/test_zrank.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sorted_set(redis: Redis): 8 | sorted_set = "sorted_set" 9 | 10 | redis.delete(sorted_set) 11 | 12 | 13 | def test_zrank(redis: Redis): 14 | sorted_set = "sorted_set" 15 | 16 | redis.zadd( 17 | sorted_set, {"apple": 10, "banana": 20, "cherry": 30, "mango": 40, "orange": 50} 18 | ) 19 | 20 | result = redis.zrank(sorted_set, "cherry") 21 | assert result == 2 22 | 23 | result = redis.zrank(sorted_set, "watermelon") 24 | assert result is None 25 | -------------------------------------------------------------------------------- /tests/commands/sortedSet/test_zrem.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sorted_set(redis: Redis): 8 | sorted_set = "sorted_set" 9 | 10 | redis.delete(sorted_set) 11 | 12 | 13 | def test_zrem(redis: Redis): 14 | sorted_set = "sorted_set" 15 | 16 | redis.zadd( 17 | sorted_set, {"apple": 10, "banana": 20, "cherry": 30, "mango": 40, "orange": 50} 18 | ) 19 | 20 | result = redis.zrem(sorted_set, "banana", "cherry") 21 | assert result == 2 22 | 23 | assert redis.zscore(sorted_set, "banana") is None 24 | 25 | result = redis.zrem(sorted_set, "watermelon") 26 | assert result == 0 27 | -------------------------------------------------------------------------------- /tests/commands/sortedSet/test_zremrangebylex.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sorted_set(redis: Redis): 8 | sorted_set = "sorted_set" 9 | 10 | redis.delete(sorted_set) 11 | 12 | 13 | def test_zremrangebylex(redis: Redis): 14 | sorted_set = "sorted_set" 15 | 16 | redis.zadd( 17 | sorted_set, {"apple": 10, "banana": 20, "cherry": 30, "mango": 40, "orange": 50} 18 | ) 19 | 20 | result = redis.zremrangebylex(sorted_set, "[banana", "(mango") 21 | assert result == 2 22 | 23 | assert redis.zscore(sorted_set, "banana") is None 24 | assert redis.zscore(sorted_set, "cherry") is None 25 | 26 | result = redis.zremrangebylex("nonexistent_set", "[", "+") 27 | assert result == 0 28 | -------------------------------------------------------------------------------- /tests/commands/sortedSet/test_zremrangebyrank.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sorted_set(redis: Redis): 8 | sorted_set = "sorted_set" 9 | 10 | redis.delete(sorted_set) 11 | 12 | 13 | def test_zremrangebyrank(redis: Redis): 14 | sorted_set = "sorted_set" 15 | 16 | redis.zadd( 17 | sorted_set, {"apple": 10, "banana": 20, "cherry": 30, "mango": 40, "orange": 50} 18 | ) 19 | 20 | result = redis.zremrangebyrank(sorted_set, 1, 3) 21 | assert result == 3 22 | 23 | assert redis.zscore(sorted_set, "banana") is None 24 | assert redis.zscore(sorted_set, "cherry") is None 25 | assert redis.zscore(sorted_set, "mango") is None 26 | 27 | result = redis.zremrangebyrank("nonexistent_set", 0, -1) 28 | assert result == 0 29 | -------------------------------------------------------------------------------- /tests/commands/sortedSet/test_zremrangebyscore.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sorted_set(redis: Redis): 8 | sorted_set = "sorted_set" 9 | 10 | redis.delete(sorted_set) 11 | 12 | 13 | def test_zremrangebyscore(redis: Redis): 14 | sorted_set = "sorted_set" 15 | 16 | redis.zadd( 17 | sorted_set, {"apple": 10, "banana": 20, "cherry": 30, "mango": 40, "orange": 50} 18 | ) 19 | 20 | result = redis.zremrangebyscore(sorted_set, 20, 40) 21 | assert result == 3 22 | 23 | assert redis.zscore(sorted_set, "banana") is None 24 | assert redis.zscore(sorted_set, "cherry") is None 25 | assert redis.zscore(sorted_set, "mango") is None 26 | 27 | result = redis.zremrangebyscore("nonexistent_set", 0, 100) 28 | assert result == 0 29 | -------------------------------------------------------------------------------- /tests/commands/sortedSet/test_zrevrange.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sorted_set(redis: Redis): 8 | sorted_set = "sorted_set" 9 | 10 | redis.delete(sorted_set) 11 | 12 | 13 | def test_zrevrange(redis: Redis): 14 | sorted_set = "sorted_set" 15 | 16 | redis.zadd( 17 | sorted_set, {"apple": 10, "banana": 20, "cherry": 30, "mango": 40, "orange": 50} 18 | ) 19 | 20 | result = redis.zrevrange(sorted_set, 1, 3) 21 | assert result == ["mango", "cherry", "banana"] 22 | 23 | result = redis.zrevrange(sorted_set, 1, 3, withscores=True) 24 | assert result == [("mango", 40.0), ("cherry", 30.0), ("banana", 20.0)] 25 | 26 | result = redis.zrevrange(sorted_set, 0, -1) 27 | assert result == ["orange", "mango", "cherry", "banana", "apple"] 28 | 29 | result = redis.zrevrange("nonexistent_set", 0, -1) 30 | assert result == [] 31 | -------------------------------------------------------------------------------- /tests/commands/sortedSet/test_zrevrangebylex.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sorted_set(redis: Redis): 8 | sorted_set = "sorted_set" 9 | 10 | redis.delete(sorted_set) 11 | 12 | 13 | def test_zrevrangebylex(redis: Redis): 14 | sorted_set = "sorted_set" 15 | 16 | redis.zadd( 17 | sorted_set, {"apple": 1, "banana": 2, "cherry": 3, "mango": 4, "orange": 5} 18 | ) 19 | 20 | result = redis.zrevrangebylex(sorted_set, "[orange", "[cherry") 21 | assert result == ["orange", "mango", "cherry"] 22 | 23 | result = redis.zrevrangebylex(sorted_set, "[orange", "[cherry", offset=1, count=2) 24 | assert result == ["mango", "cherry"] 25 | 26 | result = redis.zrevrangebylex(sorted_set, "+", "-") 27 | assert result == ["orange", "mango", "cherry", "banana", "apple"] 28 | 29 | result = redis.zrevrangebylex("nonexistent_set", "+", "-") 30 | assert result == [] 31 | -------------------------------------------------------------------------------- /tests/commands/sortedSet/test_zrevrangebyscore.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sorted_set(redis: Redis): 8 | sorted_set = "sorted_set" 9 | 10 | redis.delete(sorted_set) 11 | 12 | 13 | def test_zrevrangebyscore(redis: Redis): 14 | sorted_set = "sorted_set" 15 | 16 | redis.zadd( 17 | sorted_set, {"apple": 1, "banana": 2, "cherry": 3, "mango": 4, "orange": 5} 18 | ) 19 | 20 | result = redis.zrevrangebyscore(sorted_set, "+inf", "-inf") 21 | assert result == ["orange", "mango", "cherry", "banana", "apple"] 22 | 23 | result = redis.zrevrangebyscore(sorted_set, 4, 2) 24 | assert result == ["mango", "cherry", "banana"] 25 | 26 | result = redis.zrevrangebyscore( 27 | sorted_set, 4, 2, withscores=True, offset=1, count=2 28 | ) 29 | assert result == [("cherry", 3.0), ("banana", 2.0)] 30 | 31 | result = redis.zrevrangebyscore("nonexistent_set", "+inf", "-inf") 32 | assert result == [] 33 | -------------------------------------------------------------------------------- /tests/commands/sortedSet/test_zrevrank.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sorted_set(redis: Redis): 8 | sorted_set = "sorted_set" 9 | 10 | redis.delete(sorted_set) 11 | 12 | 13 | def test_zrevrank(redis: Redis): 14 | sorted_set = "sorted_set" 15 | 16 | redis.zadd( 17 | sorted_set, {"apple": 1, "banana": 2, "cherry": 3, "mango": 4, "orange": 5} 18 | ) 19 | 20 | result = redis.zrevrank(sorted_set, "banana") 21 | assert result == 3 22 | 23 | result = redis.zrevrank(sorted_set, "watermelon") 24 | assert result is None 25 | 26 | result = redis.zrevrank("nonexistent_set", "apple") 27 | assert result is None 28 | -------------------------------------------------------------------------------- /tests/commands/sortedSet/test_zscan.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sorted_set(redis: Redis): 8 | sorted_set = "sorted_set" 9 | 10 | redis.delete(sorted_set) 11 | 12 | 13 | def test_zscan(redis: Redis): 14 | sorted_set = "sorted_set" 15 | 16 | redis.zadd( 17 | sorted_set, {"apple": 1, "banana": 2, "cherry": 3, "mango": 4, "orange": 5} 18 | ) 19 | 20 | cursor = 0 21 | members = [] 22 | 23 | while True: 24 | cursor, scan_members = redis.zscan(sorted_set, cursor=cursor) 25 | 26 | members.extend(scan_members) 27 | 28 | if cursor == 0: 29 | break 30 | 31 | expected_members = [ 32 | ("apple", 1.0), 33 | ("banana", 2.0), 34 | ("cherry", 3.0), 35 | ("mango", 4.0), 36 | ("orange", 5.0), 37 | ] 38 | assert sorted(members) == sorted(expected_members) 39 | 40 | cursor = 0 41 | pattern = "m*" 42 | matching_members = [] 43 | 44 | while True: 45 | cursor, scan_members = redis.zscan(sorted_set, cursor=cursor, match=pattern) 46 | 47 | matching_members.extend(scan_members) 48 | 49 | if cursor == 0: 50 | break 51 | 52 | expected_matching_members = [("mango", 4.0)] 53 | assert matching_members == expected_matching_members 54 | -------------------------------------------------------------------------------- /tests/commands/sortedSet/test_zscore.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sorted_set(redis: Redis): 8 | sorted_set = "sorted_set" 9 | 10 | redis.delete(sorted_set) 11 | 12 | 13 | def test_zscore(redis: Redis): 14 | sorted_set = "sorted_set" 15 | 16 | redis.zadd(sorted_set, {"member1": 10, "member2": 20, "member3": 30}) 17 | 18 | score = redis.zscore(sorted_set, "member2") 19 | assert score == 20.0 20 | 21 | non_existent_score = redis.zscore(sorted_set, "nonexistent_member") 22 | assert non_existent_score is None 23 | -------------------------------------------------------------------------------- /tests/commands/sortedSet/test_zunion.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sorted_sets(redis: Redis): 8 | sorted_set1 = "sorted_set1" 9 | sorted_set2 = "sorted_set2" 10 | 11 | redis.delete(sorted_set1) 12 | redis.delete(sorted_set2) 13 | 14 | 15 | def test_zunion(redis: Redis): 16 | sorted_set1 = "sorted_set1" 17 | sorted_set2 = "sorted_set2" 18 | 19 | redis.zadd(sorted_set1, {"member1": 10, "member2": 20, "member3": 30}) 20 | 21 | redis.zadd(sorted_set2, {"member2": 5, "member3": 15, "member4": 25}) 22 | 23 | num_elements = redis.zunion(keys=[sorted_set1, sorted_set2], aggregate="MAX") 24 | assert num_elements == ["member1", "member2", "member4", "member3"] 25 | 26 | res = redis.zunion( 27 | keys=[sorted_set1, sorted_set2], 28 | aggregate="SUM", 29 | withscores=True, 30 | weights=[2, 3], 31 | ) 32 | 33 | assert isinstance(res, list) 34 | assert isinstance(res[0], tuple) 35 | 36 | assert res == [ 37 | ("member1", 20.0), 38 | ("member2", 55.0), 39 | ("member4", 75.0), 40 | ("member3", 105.0), 41 | ] 42 | -------------------------------------------------------------------------------- /tests/commands/sortedSet/test_zunionstore.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_sorted_sets(redis: Redis): 8 | sorted_set1 = "sorted_set1" 9 | sorted_set2 = "sorted_set2" 10 | destination = "union_result" 11 | 12 | redis.delete(sorted_set1) 13 | redis.delete(sorted_set2) 14 | redis.delete(destination) 15 | 16 | 17 | def test_zunionstore(redis: Redis): 18 | sorted_set1 = "sorted_set1" 19 | sorted_set2 = "sorted_set2" 20 | destination = "union_result" 21 | 22 | redis.zadd(sorted_set1, {"member1": 10, "member2": 20, "member3": 30}) 23 | 24 | redis.zadd(sorted_set2, {"member2": 5, "member3": 15, "member4": 25}) 25 | 26 | num_elements = redis.zunionstore( 27 | destination, keys=[sorted_set1, sorted_set2], aggregate="SUM" 28 | ) 29 | assert num_elements == 4 30 | 31 | assert redis.zrange(destination, 0, -1, withscores=True) == [ 32 | ("member1", 10.0), 33 | ("member2", 25.0), 34 | ("member4", 25.0), 35 | ("member3", 45.0), 36 | ] 37 | 38 | num_elements = redis.zunionstore( 39 | destination, keys=[sorted_set1, sorted_set2], aggregate="SUM", weights=[1, 2] 40 | ) 41 | assert num_elements == 4 42 | 43 | assert redis.zrange(destination, 0, -1, withscores=True) == [ 44 | ("member1", 10.0), 45 | ("member2", 30.0), 46 | ("member4", 50.0), 47 | ("member3", 60.0), 48 | ] 49 | -------------------------------------------------------------------------------- /tests/commands/string/test_append.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_key(redis: Redis): 8 | key = "mykey" 9 | redis.delete(key) 10 | 11 | 12 | def test_append(redis: Redis): 13 | key = "mykey" 14 | value = "Hello, " 15 | append_value = "world!" 16 | 17 | result = redis.append(key, value) 18 | assert result == len(value) 19 | 20 | result = redis.append(key, append_value) 21 | assert result == len(value) + len(append_value) 22 | 23 | final_value = redis.get(key) 24 | assert final_value == value + append_value 25 | -------------------------------------------------------------------------------- /tests/commands/string/test_decr.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_key(redis: Redis): 8 | key = "mykey" 9 | redis.delete(key) 10 | 11 | 12 | def test_decr(redis: Redis): 13 | key = "mykey" 14 | start_value = 10 15 | 16 | redis.set(key, start_value) 17 | 18 | result = redis.decr(key) 19 | assert result == start_value - 1 20 | 21 | final_value = redis.get(key) 22 | assert final_value == str(start_value - 1) 23 | -------------------------------------------------------------------------------- /tests/commands/string/test_decrby.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_key(redis: Redis): 8 | key = "mykey" 9 | redis.delete(key) 10 | 11 | 12 | def test_decrby(redis: Redis): 13 | key = "mykey" 14 | start_value = 10 15 | decrement_value = 3 16 | 17 | redis.set(key, start_value) 18 | 19 | result = redis.decrby(key, decrement_value) 20 | assert result == start_value - decrement_value 21 | 22 | final_value = redis.get(key) 23 | assert final_value == str(start_value - decrement_value) 24 | -------------------------------------------------------------------------------- /tests/commands/string/test_get.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_key(redis: Redis): 8 | key = "mykey" 9 | redis.delete(key) 10 | 11 | 12 | def test_get(redis: Redis): 13 | key = "mykey" 14 | value = "Hello, Redis!" 15 | 16 | redis.set(key, value) 17 | 18 | result = redis.get(key) 19 | assert result == value 20 | 21 | 22 | def test_get_none(redis: Redis): 23 | key = "non_existing_mykey" 24 | 25 | assert redis.get(key) is None 26 | -------------------------------------------------------------------------------- /tests/commands/string/test_getdel.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_key(redis: Redis): 8 | key = "mykey" 9 | redis.delete(key) 10 | 11 | 12 | def test_getdel(redis: Redis): 13 | key = "mykey" 14 | value = "Hello, Redis!" 15 | 16 | redis.set(key, value) 17 | 18 | result = redis.get(key) 19 | assert result == value 20 | 21 | result = redis.getdel(key) 22 | assert result == value 23 | 24 | assert redis.get(key) is None 25 | 26 | 27 | def test_getdel_none(redis: Redis): 28 | key = "non_existing_mykey" 29 | 30 | assert redis.getdel(key) is None 31 | -------------------------------------------------------------------------------- /tests/commands/string/test_getex.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_key(redis: Redis): 8 | key = "mykey" 9 | redis.delete(key) 10 | 11 | 12 | def test_getex(redis: Redis): 13 | key = "mykey" 14 | value = "Hello, Redis!" 15 | 16 | redis.set(key, value) 17 | 18 | assert redis.getex(key) == value 19 | assert redis.ttl(key) == -1 20 | 21 | redis.getex(key, ex=10) 22 | assert redis.ttl(key) == 10 23 | 24 | redis.getex(key, persist=True) 25 | assert redis.ttl(key) == -1 26 | -------------------------------------------------------------------------------- /tests/commands/string/test_getrange.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_key(redis: Redis): 8 | key = "mykey" 9 | redis.delete(key) 10 | yield 11 | redis.delete(key) 12 | 13 | 14 | def test_getrange(redis: Redis): 15 | key = "mykey" 16 | value = "Hello, Redis!" 17 | 18 | redis.set(key, value) 19 | 20 | result = redis.getrange(key, 7, 11) 21 | assert result == "Redis" 22 | 23 | result = redis.getrange(key, 7, -1) 24 | assert result == "Redis!" 25 | 26 | result = redis.getrange(key, 0, -1) 27 | assert result == value 28 | -------------------------------------------------------------------------------- /tests/commands/string/test_getset.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_key(redis: Redis): 8 | key = "mykey" 9 | redis.delete(key) 10 | yield 11 | redis.delete(key) 12 | 13 | 14 | def test_getset(redis: Redis): 15 | key = "mykey" 16 | initial_value = "Hello" 17 | new_value = "Goodbye" 18 | 19 | redis.set(key, initial_value) 20 | 21 | result = redis.getset(key, new_value) 22 | assert result == initial_value 23 | 24 | updated_value = redis.get(key) 25 | assert updated_value == new_value 26 | -------------------------------------------------------------------------------- /tests/commands/string/test_incr.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_key(redis: Redis): 8 | key = "mykey" 9 | redis.delete(key) 10 | yield 11 | redis.delete(key) 12 | 13 | 14 | def test_incr(redis: Redis): 15 | key = "mykey" 16 | initial_value = 5 17 | 18 | redis.set(key, initial_value) 19 | 20 | result = redis.incr(key) 21 | assert result == initial_value + 1 22 | 23 | updated_value = redis.get(key) 24 | assert updated_value == str(initial_value + 1) 25 | -------------------------------------------------------------------------------- /tests/commands/string/test_incrby.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_key(redis: Redis): 8 | key = "mykey" 9 | redis.delete(key) 10 | 11 | 12 | def test_incrby(redis: Redis): 13 | key = "mykey" 14 | initial_value = 5 15 | increment = 2 16 | 17 | redis.set(key, initial_value) 18 | 19 | result = redis.incrby(key, increment) 20 | assert result == initial_value + increment 21 | 22 | updated_value = redis.get(key) 23 | assert updated_value == str(initial_value + increment) 24 | -------------------------------------------------------------------------------- /tests/commands/string/test_incrbyfloat.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_key(redis: Redis): 8 | key = "mykey" 9 | redis.delete(key) 10 | yield 11 | redis.delete(key) 12 | 13 | 14 | def test_incrbyfloat(redis: Redis): 15 | key = "mykey" 16 | initial_value = 3.14 17 | increment = 1.23 18 | 19 | redis.set(key, initial_value) 20 | 21 | result = redis.incrbyfloat(key, increment) 22 | assert isinstance(result, float) 23 | 24 | assert result == pytest.approx(initial_value + increment) 25 | -------------------------------------------------------------------------------- /tests/commands/string/test_mget.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_keys(redis: Redis): 8 | keys = ["key1", "key2", "key3"] 9 | redis.delete(*keys) 10 | yield 11 | redis.delete(*keys) 12 | 13 | 14 | def test_mget(redis: Redis): 15 | keys = ["key1", "key2", "key3"] 16 | values = ["value1", "value2", "value3"] 17 | 18 | for key, value in zip(keys, values): 19 | redis.set(key, value) 20 | 21 | result = redis.mget(*keys) 22 | assert result == values 23 | 24 | 25 | def test_mget_non_existing(redis: Redis): 26 | keys = ["non_existing_key", "non_existing_key2"] 27 | 28 | result = redis.mget(*keys) 29 | assert result == [None, None] 30 | -------------------------------------------------------------------------------- /tests/commands/string/test_mset.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_keys(redis: Redis): 8 | keys = ["key1", "key2", "key3"] 9 | redis.delete(*keys) 10 | 11 | 12 | def test_mset(redis: Redis): 13 | key_value_pairs = {"key1": "value1", "key2": "value2", "key3": "value3"} 14 | 15 | redis.mset(key_value_pairs) 16 | 17 | for key, value in key_value_pairs.items(): 18 | assert redis.get(key) == value 19 | -------------------------------------------------------------------------------- /tests/commands/string/test_msetnx.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_keys(redis: Redis): 8 | keys = ["key1", "key2", "key3"] 9 | redis.delete(*keys) 10 | yield 11 | redis.delete(*keys) 12 | 13 | 14 | def test_msetnx_all_keys_do_not_exist(redis: Redis): 15 | key_value_pairs = {"key1": "value1", "key2": "value2", "key3": "value3"} 16 | 17 | result = redis.msetnx(key_value_pairs) 18 | 19 | assert result is True 20 | 21 | for key, value in key_value_pairs.items(): 22 | assert redis.get(key) == value 23 | 24 | 25 | def test_msetnx_some_keys_exist(redis: Redis): 26 | key_value_pairs = {"key1": "value1", "key2": "value2", "key3": "value3"} 27 | 28 | redis.set("key2", "existing_value") 29 | 30 | result = redis.msetnx(key_value_pairs) 31 | 32 | assert result is False 33 | assert redis.get("key1") is None 34 | assert redis.get("key2") == "existing_value" 35 | assert redis.get("key3") is None 36 | -------------------------------------------------------------------------------- /tests/commands/string/test_psetex.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | import pytest 4 | 5 | from upstash_redis import Redis 6 | 7 | 8 | @pytest.fixture(autouse=True) 9 | def flush_key(redis: Redis): 10 | key = "mykey" 11 | redis.delete(key) 12 | yield 13 | redis.delete(key) 14 | 15 | 16 | def test_psetex(redis: Redis): 17 | key = "mykey" 18 | value = "myvalue" 19 | expiration_ms = 1000 20 | 21 | result = redis.psetex(key, expiration_ms, value) 22 | 23 | assert result is True 24 | assert redis.get(key) == value 25 | assert redis.pttl(key) > 0 26 | 27 | # Wait for the key to expire 28 | time.sleep(2) 29 | 30 | assert redis.get(key) is None 31 | -------------------------------------------------------------------------------- /tests/commands/string/test_set.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_key(redis: Redis): 8 | key = "mykey" 9 | redis.delete(key) 10 | yield 11 | redis.delete(key) 12 | 13 | 14 | def test_set(redis: Redis): 15 | key = "mykey" 16 | value = "myvalue" 17 | ex_seconds = 10 18 | 19 | result = redis.set(key, value, ex=ex_seconds) 20 | 21 | assert result is True 22 | assert redis.get(key) == value 23 | assert redis.ttl(key) == ex_seconds 24 | 25 | 26 | def test_set_with_get(redis: Redis): 27 | key = "mykey" 28 | old_value = "old-value" 29 | value = "new-value" 30 | 31 | redis.set(key, old_value) 32 | 33 | result = redis.set(key, value, get=True) 34 | 35 | assert result == old_value 36 | assert redis.get(key) == value 37 | 38 | 39 | def test_set_invalid_parameters(redis: Redis): 40 | key = "mykey" 41 | value = "myvalue" 42 | ex_seconds = 10 43 | 44 | with pytest.raises(Exception): 45 | redis.set(key, value, ex=ex_seconds, pxat=ex_seconds) 46 | -------------------------------------------------------------------------------- /tests/commands/string/test_setex.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_key(redis: Redis): 8 | key = "mykey" 9 | redis.delete(key) 10 | yield 11 | redis.delete(key) 12 | 13 | 14 | def test_setex(redis: Redis): 15 | key = "mykey" 16 | value = "myvalue" 17 | ex_seconds = 10 18 | 19 | result = redis.setex(key, ex_seconds, value) 20 | 21 | assert result is True 22 | assert redis.get(key) == value 23 | assert redis.ttl(key) == ex_seconds 24 | -------------------------------------------------------------------------------- /tests/commands/string/test_setnx.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_key(redis: Redis): 8 | key = "mykey" 9 | redis.delete(key) 10 | yield 11 | redis.delete(key) 12 | 13 | 14 | def test_setnx(redis: Redis): 15 | key = "mykey" 16 | value = "myvalue" 17 | 18 | result = redis.setnx(key, value) 19 | 20 | assert result is True 21 | assert redis.get(key) == value 22 | 23 | # Try setting the key again 24 | result = redis.setnx(key, "newvalue") 25 | assert result is False 26 | assert redis.get(key) == value 27 | -------------------------------------------------------------------------------- /tests/commands/string/test_setrange.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_key(redis: Redis): 8 | key = "mykey" 9 | redis.delete(key) 10 | 11 | 12 | def test_setrange(redis: Redis): 13 | key = "mykey" 14 | value = "Hello, World!" 15 | 16 | redis.set(key, value) 17 | 18 | result = redis.setrange(key, 7, "Redis") 19 | 20 | assert result == 13 21 | 22 | expected_value = "Hello, Redis!" 23 | assert redis.get(key) == expected_value 24 | -------------------------------------------------------------------------------- /tests/commands/string/test_strlen.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_key(redis: Redis): 8 | key = "mykey" 9 | redis.delete(key) 10 | yield 11 | redis.delete(key) 12 | 13 | 14 | def test_strlen(redis: Redis): 15 | key = "mykey" 16 | value = "Hello, World!" 17 | 18 | redis.set(key, value) 19 | 20 | result = redis.strlen(key) 21 | 22 | assert result == 13 # Length of the string 23 | -------------------------------------------------------------------------------- /tests/commands/test_pipeline.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from upstash_redis import Redis 4 | 5 | 6 | @pytest.fixture(autouse=True) 7 | def flush_db(redis: Redis): 8 | redis.delete("rocket", "space", "marine") 9 | 10 | 11 | def test_pipeline(redis: Redis): 12 | pipeline = redis.pipeline() 13 | 14 | pipeline.incr("rocket") 15 | pipeline.incr("rocket") 16 | pipeline.incr("space") 17 | pipeline.incr("rocket") 18 | pipeline.incr("space") 19 | pipeline.incr("rocket") 20 | 21 | pipeline.get("rocket").get("space").get("marine") 22 | 23 | res = pipeline.exec() 24 | assert res == [1, 2, 1, 3, 2, 4, "4", "2", None] 25 | 26 | 27 | def test_multi(redis: Redis): 28 | pipeline = redis.multi() 29 | 30 | pipeline.incr("rocket") 31 | pipeline.incr("rocket") 32 | pipeline.incr("space") 33 | pipeline.incr("rocket") 34 | pipeline.incr("space") 35 | pipeline.incr("rocket") 36 | 37 | pipeline.get("rocket") 38 | pipeline.get("space") 39 | pipeline.get("marine") 40 | 41 | res = pipeline.exec() 42 | assert res == [1, 2, 1, 3, 2, 4, "4", "2", None] 43 | 44 | 45 | def test_context_manager_usage(redis: Redis): 46 | with redis.pipeline() as pipeline: 47 | pipeline.incr("rocket") 48 | pipeline.incr("rocket") 49 | pipeline.incr("space") 50 | pipeline.incr("rocket") 51 | pipeline.incr("space") 52 | pipeline.incr("rocket") 53 | result = pipeline.exec() 54 | 55 | # add a command to the pipeline which will be 56 | # removed from the pipeline when we exit the context 57 | pipeline.set("foo", "bar") 58 | 59 | assert result == [1, 2, 1, 3, 2, 4] 60 | assert len(pipeline._command_stack) == 0 # pipeline is empty 61 | 62 | # redis still works after pipeline is done 63 | result = redis.get("rocket") 64 | assert result == "4" 65 | 66 | get_pipeline = redis.pipeline() 67 | get_pipeline.get("rocket") 68 | get_pipeline.get("space") 69 | get_pipeline.get("marine") 70 | 71 | res = get_pipeline.exec() 72 | assert res == ["4", "2", None] 73 | 74 | 75 | def test_context_manager_raise(redis: Redis): 76 | """ 77 | Check that exceptions in context aren't silently ignored 78 | 79 | This can happen if we return something in __exit__ method 80 | """ 81 | with pytest.raises(Exception): 82 | with redis.pipeline() as pipeline: 83 | pipeline.incr("rocket") 84 | raise Exception("test") 85 | 86 | 87 | def test_run_pipeline_twice(redis: Redis): 88 | """ 89 | Runs a pipeline twice 90 | """ 91 | pipeline = redis.pipeline() 92 | pipeline.incr("bird") 93 | result = pipeline.exec() 94 | assert result == [1] 95 | 96 | pipeline.incrby("bird", 2) 97 | result = pipeline.exec() 98 | assert result == [3] 99 | -------------------------------------------------------------------------------- /tests/execute_on_http.py: -------------------------------------------------------------------------------- 1 | from os import environ 2 | from typing import Any, Dict 3 | 4 | import httpx 5 | 6 | from upstash_redis.typing import RESTResultT 7 | 8 | url: str = environ["UPSTASH_REDIS_REST_URL"] 9 | token: str = environ["UPSTASH_REDIS_REST_TOKEN"] 10 | 11 | headers: Dict[str, str] = {"Authorization": f"Bearer {token}"} 12 | 13 | 14 | async def execute_on_http(*command_elements: str) -> RESTResultT: 15 | async with httpx.AsyncClient(timeout=None) as client: 16 | response = await client.post(url=url, headers=headers, json=[*command_elements]) 17 | body: Dict[str, Any] = response.json() 18 | 19 | # Avoid the [] syntax to prevent KeyError from being raised. 20 | if body.get("error"): 21 | raise Exception(body.get("error")) 22 | 23 | return body["result"] 24 | 25 | 26 | def sync_execute_on_http(*command_elements: str) -> RESTResultT: 27 | response = httpx.post(url, headers=headers, json=[*command_elements]) 28 | body: Dict[str, Any] = response.json() 29 | 30 | # Avoid the [] syntax to prevent KeyError from being raised. 31 | if body.get("error"): 32 | raise Exception(body.get("error")) 33 | 34 | return body["result"] 35 | -------------------------------------------------------------------------------- /tests/test_client.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | import pytest 4 | 5 | from upstash_redis import Redis 6 | from upstash_redis.asyncio import Redis as AsyncRedis 7 | 8 | 9 | def test_redis() -> None: 10 | redis = Redis.from_env(allow_telemetry=False) 11 | assert redis.ping("hey") == "hey" 12 | redis.close() 13 | 14 | 15 | def test_redis_with_context_manager() -> None: 16 | with Redis.from_env(allow_telemetry=False) as redis: 17 | assert redis.ping("hey") == "hey" 18 | 19 | 20 | @pytest.mark.asyncio 21 | async def test_async_redis() -> None: 22 | redis = AsyncRedis.from_env(allow_telemetry=False) 23 | assert await redis.ping("hey") == "hey" 24 | await redis.close() 25 | 26 | 27 | @pytest.mark.asyncio 28 | async def test_async_redis_with_context_manager() -> None: 29 | async with AsyncRedis.from_env(allow_telemetry=False) as redis: 30 | assert await redis.ping("hey") == "hey" 31 | 32 | 33 | def test_async_redis_init_outside_coroutine() -> None: 34 | redis = AsyncRedis.from_env(allow_telemetry=False) 35 | 36 | async def coro() -> str: 37 | result = await redis.ping("hey") 38 | await redis.close() 39 | return result 40 | 41 | assert asyncio.run(coro()) == "hey" 42 | 43 | 44 | def test_async_redis_reuse_in_different_event_loops() -> None: 45 | redis = AsyncRedis.from_env(allow_telemetry=False) 46 | 47 | async def coro(close: bool) -> str: 48 | result = await redis.ping("hey") 49 | if close: 50 | await redis.close() 51 | 52 | return result 53 | 54 | assert asyncio.run(coro(False)) == "hey" 55 | assert asyncio.run(coro(True)) == "hey" 56 | -------------------------------------------------------------------------------- /tests/test_execute.py: -------------------------------------------------------------------------------- 1 | from pytest import mark 2 | 3 | from upstash_redis.asyncio import Redis 4 | 5 | 6 | @mark.asyncio 7 | async def test(async_redis: Redis): 8 | assert await async_redis.execute(["PING"]) == "PONG" 9 | -------------------------------------------------------------------------------- /upstash_redis/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = "1.4.0" 2 | 3 | from upstash_redis.asyncio.client import Redis as AsyncRedis 4 | from upstash_redis.client import Redis 5 | 6 | __all__ = ["AsyncRedis", "Redis"] 7 | -------------------------------------------------------------------------------- /upstash_redis/asyncio/__init__.py: -------------------------------------------------------------------------------- 1 | from upstash_redis.asyncio.client import Redis 2 | 3 | __all__ = ["Redis"] 4 | -------------------------------------------------------------------------------- /upstash_redis/errors.py: -------------------------------------------------------------------------------- 1 | class UpstashError(Exception): 2 | pass 3 | -------------------------------------------------------------------------------- /upstash_redis/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/upstash/redis-py/38d27f60c0219a427e4471781eaf8daeedb642a7/upstash_redis/py.typed -------------------------------------------------------------------------------- /upstash_redis/typing.py: -------------------------------------------------------------------------------- 1 | from typing import List, Literal, Union, Dict 2 | 3 | """ 4 | The type of the "result" field returned by the REST API. 5 | """ 6 | RESTResultT = Union[str, int, List, None, Literal[0, 1], Literal["OK"]] 7 | 8 | """ 9 | Types that are automatically converted into string before getting sent to redis. 10 | 11 | So they are still stored as strings. This is just a shorthand for the user. 12 | """ 13 | ValueT = Union[str, int, float, bool] 14 | 15 | JSONValueT = Union[ValueT, None, List["JSONValueT"], Dict[str, "JSONValueT"]] 16 | 17 | # "str" allows for "-inf" and "+inf". Not to be confused with the lexical min and max type (which is "str"). 18 | FloatMinMaxT = Union[float, str] 19 | --------------------------------------------------------------------------------