Init commit
This commit is contained in:
parent
98713e0b09
commit
f7a1c63324
|
@ -0,0 +1,3 @@
|
||||||
|
.idea
|
||||||
|
test.json
|
||||||
|
venv
|
|
@ -0,0 +1,41 @@
|
||||||
|
# compatibility-test-suite-for-redis
|
||||||
|
|
||||||
|
compatibility-test-suite-for-redis is used to test whether your redis-like database is compatible with Redis versions (such as
|
||||||
|
6.0, 7.0, etc.)
|
||||||
|
|
||||||
|
# Install
|
||||||
|
|
||||||
|
requires `Python 3.7` or later.
|
||||||
|
|
||||||
|
```
|
||||||
|
pip3 install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
# How to use
|
||||||
|
|
||||||
|
```
|
||||||
|
optional arguments:
|
||||||
|
-h, --help show this help message and exit
|
||||||
|
--host HOST the redis host
|
||||||
|
--port PORT the redis port
|
||||||
|
--password PASSWORD the redis password
|
||||||
|
--testfile TESTFILE the redis compatibility test cases
|
||||||
|
--specific-version {1.0.0,2.8.0,4.0.0,5.0.0,6.0.0,6.2.0,7.0.0}
|
||||||
|
the redis version
|
||||||
|
--show-failed show details of failed tests
|
||||||
|
--cluster server is a node of the Redis cluster
|
||||||
|
--ssl open ssl connection
|
||||||
|
```
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
Test whether host:port is compatible with redis 6.2.0 and display failure case:
|
||||||
|
```
|
||||||
|
$ python3 redis_compatibility_test.py -h host -p port --testfile cts.json --specific-version 6.2.0 --show-failed
|
||||||
|
test: pexpiretime command version skipped
|
||||||
|
test: persist command passed
|
||||||
|
...
|
||||||
|
test: set command passed
|
||||||
|
-------- The result of tests --------
|
||||||
|
version: 6.2.0, total tests: 17, passed: 17, rate: 100.0%
|
||||||
|
```
|
||||||
|
More examples are shown `python3 redis_compatibility_test.py -h`.
|
|
@ -0,0 +1,496 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "del command",
|
||||||
|
"command": [
|
||||||
|
"set k v",
|
||||||
|
"del k"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
"OK",
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"since": "1.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "unlink command",
|
||||||
|
"command": [
|
||||||
|
"set k v",
|
||||||
|
"unlink k"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
"OK",
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"since": "4.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "rename command",
|
||||||
|
"command": [
|
||||||
|
"set k v",
|
||||||
|
"rename k kk"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
"OK",
|
||||||
|
"OK"
|
||||||
|
],
|
||||||
|
"since": "1.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "renamenx command",
|
||||||
|
"command": [
|
||||||
|
"set k v",
|
||||||
|
"renamenx k kk"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
"OK",
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"since": "1.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "randomkey command",
|
||||||
|
"command": [
|
||||||
|
"set k v",
|
||||||
|
"randomkey"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
"OK",
|
||||||
|
"k"
|
||||||
|
],
|
||||||
|
"since": "1.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "exists command",
|
||||||
|
"command": [
|
||||||
|
"set k v",
|
||||||
|
"exists k"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
"OK",
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"since": "1.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ttl command",
|
||||||
|
"command": [
|
||||||
|
"ttl non-exists"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
-2
|
||||||
|
],
|
||||||
|
"since": "1.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pttl command",
|
||||||
|
"command": [
|
||||||
|
"pttl non-exists"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
-2
|
||||||
|
],
|
||||||
|
"since": "1.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "expire command",
|
||||||
|
"command": [
|
||||||
|
"expire non-exists 10"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"since": "1.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "expire with nx/xx",
|
||||||
|
"command": [
|
||||||
|
"set k v",
|
||||||
|
"expire k 10 NX",
|
||||||
|
"expire k 10 XX"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
"OK",
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"since": "7.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "expire with gt/lt",
|
||||||
|
"command": [
|
||||||
|
"set k v",
|
||||||
|
"expire k 10 lt",
|
||||||
|
"expire k 20 gt"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
"OK",
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"since": "7.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "expireat command",
|
||||||
|
"command": [
|
||||||
|
"expireat non-exists 10"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"since": "1.2.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "expireat with nx/xx",
|
||||||
|
"command": [
|
||||||
|
"set k v",
|
||||||
|
"expireat k 9999999998 NX",
|
||||||
|
"expireat k 9999999999 XX"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
"OK",
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"since": "7.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "expireat with gt/lt",
|
||||||
|
"command": [
|
||||||
|
"set k v",
|
||||||
|
"expireat k 9999999998 lt",
|
||||||
|
"expireat k 9999999999 gt"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
"OK",
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"since": "7.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pexpire command",
|
||||||
|
"command": [
|
||||||
|
"pexpire non-exists 10"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"since": "2.6.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pexpire with nx/xx",
|
||||||
|
"command": [
|
||||||
|
"set k v",
|
||||||
|
"pexpire k 1000 NX",
|
||||||
|
"pexpire k 1000 XX"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
"OK",
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"since": "7.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pexpire with gt/lt",
|
||||||
|
"command": [
|
||||||
|
"set k v",
|
||||||
|
"pexpire k 1000 lt",
|
||||||
|
"pexpire k 2000 gt"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
"OK",
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"since": "7.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pexpireat command",
|
||||||
|
"command": [
|
||||||
|
"pexpireat non-exists 10"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"since": "2.6.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pexpireat with nx/xx",
|
||||||
|
"command": [
|
||||||
|
"set k v",
|
||||||
|
"pexpireat k 9999999999998 NX",
|
||||||
|
"pexpireat k 9999999999999 XX"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
"OK",
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"since": "7.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pexpireat with gt/lt",
|
||||||
|
"command": [
|
||||||
|
"set k v",
|
||||||
|
"pexpireat k 9999999999998 lt",
|
||||||
|
"pexpireat k 9999999999999 gt"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
"OK",
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"since": "7.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "expiretime command",
|
||||||
|
"command": [
|
||||||
|
"expiretime non-exists"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
-2
|
||||||
|
],
|
||||||
|
"since": "7.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pexpiretime command",
|
||||||
|
"command": [
|
||||||
|
"pexpiretime non-exists"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
-2
|
||||||
|
],
|
||||||
|
"since": "7.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "persist command",
|
||||||
|
"command": [
|
||||||
|
"persist non-exists"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"since": "2.2.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "dump command",
|
||||||
|
"command": [
|
||||||
|
"dump not-exists"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
null
|
||||||
|
],
|
||||||
|
"since": "2.6.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "touch command",
|
||||||
|
"command": [
|
||||||
|
"touch not-exists"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"since": "3.2.1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "restore command",
|
||||||
|
"command": [
|
||||||
|
"restore k 0 \\x00\\x01v\\x06\\x00\\a\\xe5\\xa62\\xecm\\xb6]"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
"OK"
|
||||||
|
],
|
||||||
|
"since": "2.6.0",
|
||||||
|
"command_binary": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "restore with replace",
|
||||||
|
"command": [
|
||||||
|
"set k v",
|
||||||
|
"restore k 0 \\x00\\x01v\\x06\\x00\\a\\xe5\\xa62\\xecm\\xb6] REPLACE"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
"OK",
|
||||||
|
"OK"
|
||||||
|
],
|
||||||
|
"since": "3.0.0",
|
||||||
|
"command_binary": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "restore with absttl",
|
||||||
|
"command": [
|
||||||
|
"restore k 0 \\x00\\x01v\\x06\\x00\\a\\xe5\\xa62\\xecm\\xb6] ABSTTL",
|
||||||
|
"ttl k"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
"OK",
|
||||||
|
-1
|
||||||
|
],
|
||||||
|
"since": "5.0.0",
|
||||||
|
"command_binary": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "restore with idletime",
|
||||||
|
"command": [
|
||||||
|
"restore k 0 \\x00\\x01v\\x06\\x00\\a\\xe5\\xa62\\xecm\\xb6] IDLETIME 1000"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
"OK"
|
||||||
|
],
|
||||||
|
"since": "5.0.0",
|
||||||
|
"command_binary": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "scan command",
|
||||||
|
"command": [
|
||||||
|
"set k v",
|
||||||
|
"scan 0"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
"OK",
|
||||||
|
[
|
||||||
|
"0",
|
||||||
|
[
|
||||||
|
"k"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"since": "2.8.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "scan with type",
|
||||||
|
"command": [
|
||||||
|
"geoadd geokey 0 0 value",
|
||||||
|
"scan 0 type zset"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
1,
|
||||||
|
[
|
||||||
|
"0",
|
||||||
|
[
|
||||||
|
"geokey"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"since": "6.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "keys command",
|
||||||
|
"command": [
|
||||||
|
"mset firstname Jack lastname Stuntman age 35",
|
||||||
|
"keys a??"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
"OK",
|
||||||
|
[
|
||||||
|
"age"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"since": "1.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "move command",
|
||||||
|
"command": [
|
||||||
|
"set k v",
|
||||||
|
"move k 1"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
"OK",
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"since": "1.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "copy command",
|
||||||
|
"command": [
|
||||||
|
"set k v",
|
||||||
|
"copy k kk",
|
||||||
|
"get kk"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
"OK",
|
||||||
|
1,
|
||||||
|
"v"
|
||||||
|
],
|
||||||
|
"since": "6.2.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "type command",
|
||||||
|
"command": [
|
||||||
|
"set k v",
|
||||||
|
"type k"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
"OK",
|
||||||
|
"string"
|
||||||
|
],
|
||||||
|
"since": "1.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "wait command",
|
||||||
|
"command": [
|
||||||
|
"wait 0 1000"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"since": "3.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sort command",
|
||||||
|
"command": [
|
||||||
|
"lpush list 5 3 4 1 2",
|
||||||
|
"sort list"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
5,
|
||||||
|
[
|
||||||
|
"1",
|
||||||
|
"2",
|
||||||
|
"3",
|
||||||
|
"4",
|
||||||
|
"5"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"since": "1.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sort_ro command",
|
||||||
|
"command": [
|
||||||
|
"lpush list 5 3 4 1 2",
|
||||||
|
"sort_ro list"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
5,
|
||||||
|
[
|
||||||
|
"1",
|
||||||
|
"2",
|
||||||
|
"3",
|
||||||
|
"4",
|
||||||
|
"5"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"since": "7.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "migrate command",
|
||||||
|
"command": [
|
||||||
|
"MIGRATE 0.0.0.0 6379 k 0 0"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
"NOKEY"
|
||||||
|
],
|
||||||
|
"since": "2.6.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "set command",
|
||||||
|
"command": [
|
||||||
|
"set k v"
|
||||||
|
],
|
||||||
|
"result": [
|
||||||
|
"OK"
|
||||||
|
],
|
||||||
|
"since": "1.0.0"
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,207 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import argparse
|
||||||
|
import redis
|
||||||
|
import json
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import List, Dict
|
||||||
|
|
||||||
|
EXAMPLE = """
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
Run tests without specifying a version
|
||||||
|
python3 redis_compatibility_test.py --testfile cts.json
|
||||||
|
|
||||||
|
Run the test for compatibility with Redis 6.2.0
|
||||||
|
python3 redis_compatibility_test.py --testfile cts.json --specific-version 6.2.0
|
||||||
|
|
||||||
|
Run the test whether it is compatible with Redis 6.2.0, and print the failure case
|
||||||
|
python3 redis_compatibility_test.py --testfile cts.json --specific-version 6.2.0 --show-failed
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class FailedTest:
|
||||||
|
name: str
|
||||||
|
reason: object
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class TestResult:
|
||||||
|
total: int
|
||||||
|
passed: int
|
||||||
|
failed: List[FailedTest]
|
||||||
|
|
||||||
|
|
||||||
|
r: redis.Redis = None
|
||||||
|
g_results: Dict[str, TestResult] = {}
|
||||||
|
|
||||||
|
|
||||||
|
def report_result():
|
||||||
|
print(f"-------- The result of tests --------")
|
||||||
|
if args.specific_version:
|
||||||
|
total = passed = 0
|
||||||
|
failed: List[FailedTest] = []
|
||||||
|
for v, t in g_results.items():
|
||||||
|
total += t.total
|
||||||
|
passed += t.passed
|
||||||
|
failed.extend(t.failed)
|
||||||
|
print(f"version: {args.specific_version}, total tests: {total}, passed: {passed}, "
|
||||||
|
f"rate: {repr(passed / total * 100)}%")
|
||||||
|
if args.show_failed and len(failed) != 0:
|
||||||
|
print(f"This is failed tests for {args.specific_version}:")
|
||||||
|
print(failed)
|
||||||
|
else:
|
||||||
|
for v, t in sorted(g_results.items()):
|
||||||
|
print(f"version: {v}, total tests: {t.total}, passed: {t.passed}, "
|
||||||
|
f"rate: {repr(t.passed / t.total * 100)}%")
|
||||||
|
if args.show_failed and len(t.failed) != 0:
|
||||||
|
print(f"This is failed tests for {v}:")
|
||||||
|
print(t.failed)
|
||||||
|
|
||||||
|
|
||||||
|
def is_equal(left, right):
|
||||||
|
if type(left) is bytes and type(right) is str:
|
||||||
|
return left.decode() == right
|
||||||
|
elif type(left) is str and type(right) is bytes:
|
||||||
|
return left == right.decode()
|
||||||
|
else:
|
||||||
|
return left == right
|
||||||
|
|
||||||
|
|
||||||
|
def test_passed(result):
|
||||||
|
print("passed")
|
||||||
|
result.total += 1
|
||||||
|
result.passed += 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_failed(result, name, e):
|
||||||
|
print("failed")
|
||||||
|
result.total += 1
|
||||||
|
result.failed.append(FailedTest(name=name, reason=e))
|
||||||
|
|
||||||
|
|
||||||
|
def trans_result_to_bytes(result):
|
||||||
|
if type(result) is str:
|
||||||
|
return result.encode()
|
||||||
|
if type(result) is list:
|
||||||
|
for i in range(len(result)):
|
||||||
|
result[i] = trans_result_to_bytes(result[i])
|
||||||
|
if type(result) is map:
|
||||||
|
for k, v in result.items():
|
||||||
|
result[k.encode()] = trans_result_to_bytes(v)
|
||||||
|
del result[k]
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def trans_cmd(test, cmd):
|
||||||
|
if 'command_binary' in test:
|
||||||
|
array = bytearray()
|
||||||
|
i = 0
|
||||||
|
while i < len(cmd):
|
||||||
|
if cmd[i] == '\\' and cmd[i + 1] == '\\':
|
||||||
|
array.append(92)
|
||||||
|
i += 2
|
||||||
|
elif cmd[i] == '\\' and cmd[i + 1] == '"':
|
||||||
|
array.append(34)
|
||||||
|
i += 2
|
||||||
|
elif cmd[i] == '\\' and cmd[i + 1] == 'n':
|
||||||
|
array.append(10)
|
||||||
|
i += 2
|
||||||
|
elif cmd[i] == '\\' and cmd[i + 1] == 'r':
|
||||||
|
array.append(13)
|
||||||
|
i += 2
|
||||||
|
elif cmd[i] == '\\' and cmd[i + 1] == 't':
|
||||||
|
array.append(9)
|
||||||
|
i += 2
|
||||||
|
elif cmd[i] == '\\' and cmd[i + 1] == 'a':
|
||||||
|
array.append(7)
|
||||||
|
i += 2
|
||||||
|
elif cmd[i] == '\\' and cmd[i + 1] == 'b':
|
||||||
|
array.append(8)
|
||||||
|
i += 2
|
||||||
|
elif cmd[i] == '\\' and cmd[i + 1] == 'x':
|
||||||
|
array.append(int(cmd[i + 2], 16) * 16 + int(cmd[i + 3], 16))
|
||||||
|
i += 4
|
||||||
|
else:
|
||||||
|
array.append(ord(cmd[i]))
|
||||||
|
i += 1
|
||||||
|
return bytes(array)
|
||||||
|
else:
|
||||||
|
return cmd
|
||||||
|
|
||||||
|
|
||||||
|
def run_test(test):
|
||||||
|
name = test['name']
|
||||||
|
print(f"test: {name}", end=" ")
|
||||||
|
# if test need skipped
|
||||||
|
if 'skipped' in test:
|
||||||
|
print("skipped")
|
||||||
|
return
|
||||||
|
|
||||||
|
# high version test
|
||||||
|
since = test['since']
|
||||||
|
if args.specific_version and since > args.specific_version:
|
||||||
|
print("version skipped")
|
||||||
|
return
|
||||||
|
if since not in g_results:
|
||||||
|
g_results[since] = TestResult(total=0, passed=0, failed=[])
|
||||||
|
|
||||||
|
r.flushall()
|
||||||
|
command = test['command']
|
||||||
|
result = test['result']
|
||||||
|
trans_result_to_bytes(result)
|
||||||
|
try:
|
||||||
|
for idx, cmd in enumerate(command):
|
||||||
|
ret = r.execute_command(trans_cmd(test, cmd))
|
||||||
|
if result[idx] != ret:
|
||||||
|
test_failed(g_results[since], name, f"expected: {result[idx]}, result: {ret}")
|
||||||
|
return
|
||||||
|
test_passed(g_results[since])
|
||||||
|
except Exception as e:
|
||||||
|
test_failed(g_results[since], name, e)
|
||||||
|
|
||||||
|
|
||||||
|
def run_compatibility_tests(filename):
|
||||||
|
with open(filename, "r") as f:
|
||||||
|
tests = f.read()
|
||||||
|
tests_array = json.loads(tests)
|
||||||
|
for test in tests_array:
|
||||||
|
run_test(test)
|
||||||
|
|
||||||
|
|
||||||
|
def create_client(args):
|
||||||
|
global r
|
||||||
|
if args.cluster:
|
||||||
|
print(f"Connecting to {args.host}:{args.port} use cluster client")
|
||||||
|
r = redis.RedisCluster(host=args.host, port=args.port, password=args.password, ssl=args.ssl)
|
||||||
|
assert r.ping()
|
||||||
|
else:
|
||||||
|
print(f"Connecting to {args.host}:{args.port} use standalone client")
|
||||||
|
r = redis.Redis(host=args.host, port=args.port, password=args.password, ssl=args.ssl)
|
||||||
|
assert r.ping()
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
parser = argparse.ArgumentParser(prog="redis_compatibility_test",
|
||||||
|
description="redis_compatibility_test is used to test whether your redis-like "
|
||||||
|
"database is compatible with Redis versions (such as 6.0, 7.0, etc.)",
|
||||||
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
|
epilog=EXAMPLE)
|
||||||
|
parser.add_argument("--host", help="the redis host", default="127.0.0.1")
|
||||||
|
parser.add_argument("--port", help="the redis port", default=6379, type=int)
|
||||||
|
parser.add_argument("--password", help="the redis password", default="")
|
||||||
|
parser.add_argument("--testfile", help="the redis compatibility test cases", required=True)
|
||||||
|
parser.add_argument("--specific-version", dest="specific_version", help="the redis version",
|
||||||
|
choices=['1.0.0', '2.8.0', '4.0.0', '5.0.0', '6.0.0', '6.2.0', '7.0.0'])
|
||||||
|
parser.add_argument("--show-failed", dest="show_failed", help="show details of failed tests", default=False,
|
||||||
|
action="store_true")
|
||||||
|
parser.add_argument("--cluster", help="server is a node of the Redis cluster", default=False, action="store_true")
|
||||||
|
parser.add_argument("--ssl", help="open ssl connection", default=False, action="store_true")
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
args = parse_args()
|
||||||
|
create_client(args)
|
||||||
|
run_compatibility_tests(args.testfile)
|
||||||
|
report_result()
|
|
@ -0,0 +1 @@
|
||||||
|
redis>=4.3.4
|
Loading…
Reference in New Issue